mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-01-24 00:00:05 -05:00
Fixed JavaDoc errors to enable Java 8 build.
This commit is contained in:
@@ -1,110 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ImageConsumer;
|
||||
import java.awt.image.ImageProducer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AbstractImageSource
|
||||
* <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/image/AbstractImageSource.java#1 $
|
||||
*/
|
||||
public abstract class AbstractImageSource implements ImageProducer {
|
||||
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int xOff;
|
||||
protected int yOff;
|
||||
|
||||
// ImageProducer interface
|
||||
public void addConsumer(final ImageConsumer pConsumer) {
|
||||
if (consumers.contains(pConsumer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumers.add(pConsumer);
|
||||
|
||||
try {
|
||||
initConsumer(pConsumer);
|
||||
sendPixels(pConsumer);
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||
|
||||
// Get rid of "sticky" consumers...
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
removeConsumer(pConsumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConsumer(final ImageConsumer pConsumer) {
|
||||
consumers.remove(pConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation silently ignores this instruction. If pixel data is
|
||||
* not in TDLR order by default, subclasses must override this method.
|
||||
*
|
||||
* @param pConsumer the consumer that requested the resend
|
||||
*
|
||||
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
||||
*/
|
||||
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void startProduction(final ImageConsumer pConsumer) {
|
||||
addConsumer(pConsumer);
|
||||
}
|
||||
|
||||
public boolean isConsumer(final ImageConsumer pConsumer) {
|
||||
return consumers.contains(pConsumer);
|
||||
}
|
||||
|
||||
protected abstract void initConsumer(ImageConsumer pConsumer);
|
||||
|
||||
protected abstract void sendPixels(ImageConsumer pConsumer);
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ImageConsumer;
|
||||
import java.awt.image.ImageProducer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AbstractImageSource
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public abstract class AbstractImageSource implements ImageProducer {
|
||||
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int xOff;
|
||||
protected int yOff;
|
||||
|
||||
// ImageProducer interface
|
||||
public void addConsumer(final ImageConsumer pConsumer) {
|
||||
if (consumers.contains(pConsumer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumers.add(pConsumer);
|
||||
|
||||
try {
|
||||
initConsumer(pConsumer);
|
||||
sendPixels(pConsumer);
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||
|
||||
// Get rid of "sticky" consumers...
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
removeConsumer(pConsumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConsumer(final ImageConsumer pConsumer) {
|
||||
consumers.remove(pConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation silently ignores this instruction. If pixel data is
|
||||
* not in TDLR order by default, subclasses must override this method.
|
||||
*
|
||||
* @param pConsumer the consumer that requested the resend
|
||||
*
|
||||
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
||||
*/
|
||||
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void startProduction(final ImageConsumer pConsumer) {
|
||||
addConsumer(pConsumer);
|
||||
}
|
||||
|
||||
public boolean isConsumer(final ImageConsumer pConsumer) {
|
||||
return consumers.contains(pConsumer);
|
||||
}
|
||||
|
||||
protected abstract void initConsumer(ImageConsumer pConsumer);
|
||||
|
||||
protected abstract void sendPixels(ImageConsumer pConsumer);
|
||||
}
|
||||
|
||||
@@ -1,170 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the contrast and brightness of an image.
|
||||
* <p/>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||
* <p/>
|
||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* 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>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @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 {
|
||||
|
||||
// TODO: Replace with RescaleOp?
|
||||
|
||||
// This filter can filter IndexColorModel, as it is does not depend on
|
||||
// the pixels' location
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
// Use a pre-calculated lookup table for performance
|
||||
private final int[] LUT;
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with default values
|
||||
* ({@code brightness=0.3, contrast=0.3}).
|
||||
* <p/>
|
||||
* This will slightly increase both brightness and contrast.
|
||||
*/
|
||||
public BrightnessContrastFilter() {
|
||||
this(0.3f, 0.3f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with the given values for brightness
|
||||
* and contrast.
|
||||
* <p/>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||
* <p/>
|
||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
*
|
||||
* @param pBrightness adjust the brightness of the image, in the range
|
||||
* {@code -2.0,..,0.0,..,2.0}.
|
||||
* @param pContrast adjust the contrast of the image, in the range
|
||||
* {@code -1.0,..,0.0,..,1.0}.
|
||||
*/
|
||||
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
||||
LUT = createLUT(pBrightness, pContrast);
|
||||
}
|
||||
|
||||
private static int[] createLUT(float pBrightness, float pContrast) {
|
||||
int[] lut = new int[256];
|
||||
|
||||
// Hmmm.. This approximates Photoshop values.. Not good though..
|
||||
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
|
||||
|
||||
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
|
||||
double brightness = pBrightness + 1.0;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
|
||||
}
|
||||
|
||||
// Special case, to ensure only primary colors for max contrast
|
||||
if (pContrast == 1f) {
|
||||
lut[127] = lut[126];
|
||||
}
|
||||
|
||||
return lut;
|
||||
}
|
||||
|
||||
private static int clamp(int i) {
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (i > 255) {
|
||||
return 255;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel, adjusting brightness and contrast according to this
|
||||
* filter.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in 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
|
||||
int r = pARGB >> 16 & 0xFF;
|
||||
int g = pARGB >> 8 & 0xFF;
|
||||
int b = pARGB & 0xFF;
|
||||
|
||||
// Scale to new contrast
|
||||
r = LUT[r];
|
||||
g = LUT[g];
|
||||
b = LUT[b];
|
||||
|
||||
// Return ARGB pixel, leave transparency as is
|
||||
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the contrast and brightness of an image.
|
||||
* <p>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* 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}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
* </p>
|
||||
*
|
||||
* @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/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 {
|
||||
|
||||
// TODO: Replace with RescaleOp?
|
||||
|
||||
// This filter can filter IndexColorModel, as it is does not depend on
|
||||
// the pixels' location
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
// Use a pre-calculated lookup table for performance
|
||||
private final int[] LUT;
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with default values
|
||||
* ({@code brightness=0.3, contrast=0.3}).
|
||||
* <p>
|
||||
* This will slightly increase both brightness and contrast.
|
||||
* </p>
|
||||
*/
|
||||
public BrightnessContrastFilter() {
|
||||
this(0.3f, 0.3f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with the given values for brightness
|
||||
* and contrast.
|
||||
* <p>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* 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}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
* </p>
|
||||
*
|
||||
* @param pBrightness adjust the brightness of the image, in the range
|
||||
* {@code -2.0,..,0.0,..,2.0}.
|
||||
* @param pContrast adjust the contrast of the image, in the range
|
||||
* {@code -1.0,..,0.0,..,1.0}.
|
||||
*/
|
||||
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
||||
LUT = createLUT(pBrightness, pContrast);
|
||||
}
|
||||
|
||||
private static int[] createLUT(float pBrightness, float pContrast) {
|
||||
int[] lut = new int[256];
|
||||
|
||||
// Hmmm.. This approximates Photoshop values.. Not good though..
|
||||
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
|
||||
|
||||
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
|
||||
double brightness = pBrightness + 1.0;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
|
||||
}
|
||||
|
||||
// Special case, to ensure only primary colors for max contrast
|
||||
if (pContrast == 1f) {
|
||||
lut[127] = lut[126];
|
||||
}
|
||||
|
||||
return lut;
|
||||
}
|
||||
|
||||
private static int clamp(int i) {
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (i > 255) {
|
||||
return 255;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel, adjusting brightness and contrast according to this
|
||||
* filter.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in 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
|
||||
int r = pARGB >> 16 & 0xFF;
|
||||
int g = pARGB >> 8 & 0xFF;
|
||||
int b = pARGB & 0xFF;
|
||||
|
||||
// Scale to new contrast
|
||||
r = LUT[r];
|
||||
g = LUT[g];
|
||||
b = LUT[b];
|
||||
|
||||
// Return ARGB pixel, leave transparency as is
|
||||
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,14 +44,16 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
* A faster, lighter and easier way to convert an {@code Image} to a
|
||||
* {@code BufferedImage} than using a {@code PixelGrabber}.
|
||||
* Clients may provide progress listeners to monitor conversion progress.
|
||||
* <p/>
|
||||
* <p>
|
||||
* Supports source image subsampling and source region extraction.
|
||||
* Supports source images with 16 bit {@link ColorModel} and
|
||||
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
|
||||
* 32 bit/TYPE_INT.
|
||||
* <p/>
|
||||
* </p>
|
||||
* <p>
|
||||
* NOTE: Does not support images with more than one {@code ColorModel} or
|
||||
* different types of pixel data. This is not very common.
|
||||
* </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/image/BufferedImageFactory.java#1 $
|
||||
|
||||
@@ -1,92 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
||||
* <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/image/BufferedImageIcon.java#2 $
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private int width;
|
||||
private int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
||||
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
|
||||
image = Validate.notNull(pImage, "image");
|
||||
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
||||
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
||||
|
||||
fast = useFastRendering;
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (fast || !(g instanceof Graphics2D)) {
|
||||
//System.out.println("Scaling fast");
|
||||
g.drawImage(image, x, y, width, height, null);
|
||||
}
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, xform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private int width;
|
||||
private int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
||||
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
|
||||
image = Validate.notNull(pImage, "image");
|
||||
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
||||
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
||||
|
||||
fast = useFastRendering;
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (fast || !(g instanceof Graphics2D)) {
|
||||
//System.out.println("Scaling fast");
|
||||
g.drawImage(image, x, y, width, height, null);
|
||||
}
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, xform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,483 +1,485 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This {@code BufferedImageOp/RasterOp} implements basic
|
||||
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
||||
* <P/>
|
||||
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
||||
* <!-- - -
|
||||
* | |x|7|
|
||||
* - - - -
|
||||
* |3|5|1|
|
||||
* - - -->
|
||||
* <P/>
|
||||
* <TABLE border="1" cellpadding="4" cellspacing="0">
|
||||
* <TR><TD bgcolor="#000000"> </TD><TD class="TableHeadingColor"
|
||||
* align="center">X</TD><TD>7/16</TD></TR>
|
||||
* <TR><TD>3/16</TD><TD>5/16</TD><TD>1/16</TD></TR>
|
||||
* </TABLE>
|
||||
* <P/>
|
||||
* 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>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: DiffusionDither.java#1 $
|
||||
*/
|
||||
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
||||
|
||||
private static final int FS_SCALE = 1 << 8;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
protected final IndexColorModel indexColorModel;
|
||||
private boolean alternateScans = true;
|
||||
|
||||
/**
|
||||
* Creates a {@code DiffusionDither}, using the given
|
||||
* {@code IndexColorModel} for dithering into.
|
||||
*
|
||||
* @param pICM an IndexColorModel.
|
||||
*/
|
||||
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
|
||||
* {@code IndexColorModel}.
|
||||
*/
|
||||
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.
|
||||
* The default is {@code true}.
|
||||
*
|
||||
* @param pUse {@code true} if scan mode should be alternating left/right
|
||||
*/
|
||||
public void setAlternateScans(boolean pUse) {
|
||||
alternateScans = pUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code BufferedImage} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @return a compatible {@code BufferedImage}
|
||||
*
|
||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||
* an instance of {@code IndexColorModel}.
|
||||
*/
|
||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||
if (pDestCM == null) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
getICM(pSource));
|
||||
}
|
||||
else if (pDestCM instanceof IndexColorModel) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
(IndexColorModel) pDestCM);
|
||||
}
|
||||
else {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @param pSrc the source raster
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
|
||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
*
|
||||
* @param pSrc the source raster.
|
||||
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the filtered destination image. Since
|
||||
* this is not a geometric operation, the bounding box does not
|
||||
* change.
|
||||
* @param pSrc the {@code BufferedImage} to be filtered
|
||||
* @return the bounds of the filtered definition image.
|
||||
*/
|
||||
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.
|
||||
* @param pSrc the {@code Raster} to be filtered
|
||||
* @return the bounds of the filtered definition {@code Raster}.
|
||||
*/
|
||||
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.
|
||||
* Since this is not a geometric operation, the {@code srcPt}
|
||||
* will equal the {@code dstPt}.
|
||||
* @param pSrcPt a {@code Point2D} that represents a point
|
||||
* in the source image
|
||||
* @param pDstPt a {@code Point2D}that represents the location
|
||||
* in the destination
|
||||
* @return the {@code Point2D} in the destination that
|
||||
* corresponds to the specified point in the source.
|
||||
*/
|
||||
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
|
||||
// Create new Point, if needed
|
||||
if (pDstPt == null) {
|
||||
pDstPt = new Point2D.Float();
|
||||
}
|
||||
|
||||
// Copy location
|
||||
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
|
||||
|
||||
// Return dest
|
||||
return pDstPt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendering mHints for this op.
|
||||
* @return the {@code RenderingHints} object associated
|
||||
* with this op.
|
||||
*/
|
||||
public final RenderingHints getRenderingHints() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int ARGB to int triplet.
|
||||
*/
|
||||
private static int[] toRGBArray(int pARGB, int[] pBuffer) {
|
||||
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
|
||||
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
|
||||
pBuffer[2] = ((pARGB & 0x000000ff));
|
||||
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a int triplet to int ARGB.
|
||||
*/
|
||||
private static int toIntARGB(int[] pRGB) {
|
||||
return 0xff000000 // All opaque
|
||||
| (pRGB[0] << 16)
|
||||
| (pRGB[1] << 8)
|
||||
| (pRGB[2]);
|
||||
/*
|
||||
| ((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.
|
||||
*
|
||||
* @param pSource the source image
|
||||
* @param pDest the destination image
|
||||
*
|
||||
* @return the destination image, or a new image, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||
// Create destination image, if none provided
|
||||
if (pDest == null) {
|
||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||
}
|
||||
else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
|
||||
// Filter rasters
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
* {@code null}.
|
||||
*/
|
||||
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(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);
|
||||
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.
|
||||
*
|
||||
* @param pSource the source raster, assumed to be in sRGB
|
||||
* @param pDest the destination raster, may be {@code null}
|
||||
* @param pColorModel the indexed color model to use
|
||||
*
|
||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||
int width = pSource.getWidth();
|
||||
int height = pSource.getHeight();
|
||||
|
||||
// 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
|
||||
// offset from actual column position by one to allow FS to not check
|
||||
// left/right edge conditions
|
||||
int[][] currErr = new int[width + 2][3];
|
||||
int[][] nextErr = new int[width + 2][3];
|
||||
|
||||
// Random errors in [-1 .. 1] - for first row
|
||||
for (int i = 0; i < width + 2; i++) {
|
||||
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
|
||||
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];
|
||||
final int[] outRGB = new int[4];
|
||||
Object pixel = null;
|
||||
boolean forward = true;
|
||||
|
||||
// Loop through image data
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Clear out next error rows for colour errors
|
||||
for (int i = nextErr.length; --i >= 0;) {
|
||||
nextErr[i][0] = 0;
|
||||
nextErr[i][1] = 0;
|
||||
nextErr[i][2] = 0;
|
||||
}
|
||||
|
||||
// Set up start column and limit
|
||||
int x;
|
||||
int limit;
|
||||
if (forward) {
|
||||
x = 0;
|
||||
limit = width;
|
||||
}
|
||||
else {
|
||||
x = width - 1;
|
||||
limit = -1;
|
||||
}
|
||||
|
||||
// TODO: Use getPixels instead of getPixel for better performance?
|
||||
|
||||
// Loop over row
|
||||
while (true) {
|
||||
// Get RGB from original raster
|
||||
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
|
||||
pSource.getPixel(x, y, inRGB);
|
||||
|
||||
// Get error for this pixel & add error to rgb
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Make a 28.4 FP number, add Error (with fraction),
|
||||
// rounding and truncate to int
|
||||
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
|
||||
|
||||
// Clamp
|
||||
if (inRGB[i] > 255) {
|
||||
inRGB[i] = 255;
|
||||
}
|
||||
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.
|
||||
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
|
||||
|
||||
// ...set it...
|
||||
pDest.setDataElements(x, y, pixel);
|
||||
|
||||
// ..and get back the closet match
|
||||
pDest.getPixel(x, y, outRGB);
|
||||
|
||||
// Convert the value to default sRGB
|
||||
// Should work for all transfertypes supported by IndexColorModel
|
||||
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
|
||||
|
||||
// Find diff
|
||||
diff[0] = inRGB[0] - outRGB[0];
|
||||
diff[1] = inRGB[1] - outRGB[1];
|
||||
diff[2] = inRGB[2] - outRGB[2];
|
||||
|
||||
// Apply F-S error diffusion
|
||||
// Serpentine scan: left-right
|
||||
if (forward) {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x + 1)
|
||||
currErr[x + 2][0] += diff[0] * 7;
|
||||
currErr[x + 2][1] += diff[1] * 7;
|
||||
currErr[x + 2][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x - 1)
|
||||
nextErr[x][0] += diff[0] * 3;
|
||||
nextErr[x][1] += diff[1] * 3;
|
||||
nextErr[x][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x + 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x + 2][0] += diff[0]; // * 1;
|
||||
nextErr[x + 2][1] += diff[1]; // * 1;
|
||||
nextErr[x + 2][2] += diff[2]; // * 1;
|
||||
|
||||
// Next
|
||||
x++;
|
||||
|
||||
// Done?
|
||||
if (x >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x - 1)
|
||||
currErr[x][0] += diff[0] * 7;
|
||||
currErr[x][1] += diff[1] * 7;
|
||||
currErr[x][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x + 1)
|
||||
nextErr[x + 2][0] += diff[0] * 3;
|
||||
nextErr[x + 2][1] += diff[1] * 3;
|
||||
nextErr[x + 2][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x - 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x][0] += diff[0]; // * 1;
|
||||
nextErr[x][1] += diff[1]; // * 1;
|
||||
nextErr[x][2] += diff[2]; // * 1;
|
||||
|
||||
// Previous
|
||||
x--;
|
||||
|
||||
// Done?
|
||||
if (x <= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make next error info current for next iteration
|
||||
int[][] temperr;
|
||||
temperr = currErr;
|
||||
currErr = nextErr;
|
||||
nextErr = temperr;
|
||||
|
||||
// Toggle direction
|
||||
if (alternateScans) {
|
||||
forward = !forward;
|
||||
}
|
||||
}
|
||||
|
||||
return pDest;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This {@code BufferedImageOp/RasterOp} implements basic
|
||||
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
||||
* <p>
|
||||
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
||||
* <!-- - -
|
||||
* | |x|7|
|
||||
* - - - -
|
||||
* |3|5|1|
|
||||
* - - -->
|
||||
* </p>
|
||||
* <table border="1" cellpadding="4" cellspacing="0">
|
||||
* <caption>Floyd-Steinberg error-diffusion weights</caption>
|
||||
* <tr><td bgcolor="#000000"> </td><td class="TableHeadingColor"
|
||||
* align="center">x</td><td>7/16</td></tr>
|
||||
* <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
||||
* for more information.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: DiffusionDither.java#1 $
|
||||
*/
|
||||
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
||||
|
||||
private static final int FS_SCALE = 1 << 8;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
protected final IndexColorModel indexColorModel;
|
||||
private boolean alternateScans = true;
|
||||
|
||||
/**
|
||||
* Creates a {@code DiffusionDither}, using the given
|
||||
* {@code IndexColorModel} for dithering into.
|
||||
*
|
||||
* @param pICM an IndexColorModel.
|
||||
*/
|
||||
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
|
||||
* {@code IndexColorModel}.
|
||||
*/
|
||||
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.
|
||||
* The default is {@code true}.
|
||||
*
|
||||
* @param pUse {@code true} if scan mode should be alternating left/right
|
||||
*/
|
||||
public void setAlternateScans(boolean pUse) {
|
||||
alternateScans = pUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code BufferedImage} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @return a compatible {@code BufferedImage}
|
||||
*
|
||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||
* an instance of {@code IndexColorModel}.
|
||||
*/
|
||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||
if (pDestCM == null) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
getICM(pSource));
|
||||
}
|
||||
else if (pDestCM instanceof IndexColorModel) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
(IndexColorModel) pDestCM);
|
||||
}
|
||||
else {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @param pSrc the source raster
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
|
||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
*
|
||||
* @param pSrc the source raster.
|
||||
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the filtered destination image. Since
|
||||
* this is not a geometric operation, the bounding box does not
|
||||
* change.
|
||||
* @param pSrc the {@code BufferedImage} to be filtered
|
||||
* @return the bounds of the filtered definition image.
|
||||
*/
|
||||
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.
|
||||
* @param pSrc the {@code Raster} to be filtered
|
||||
* @return the bounds of the filtered definition {@code Raster}.
|
||||
*/
|
||||
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.
|
||||
* Since this is not a geometric operation, the {@code srcPt}
|
||||
* will equal the {@code dstPt}.
|
||||
* @param pSrcPt a {@code Point2D} that represents a point
|
||||
* in the source image
|
||||
* @param pDstPt a {@code Point2D}that represents the location
|
||||
* in the destination
|
||||
* @return the {@code Point2D} in the destination that
|
||||
* corresponds to the specified point in the source.
|
||||
*/
|
||||
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
|
||||
// Create new Point, if needed
|
||||
if (pDstPt == null) {
|
||||
pDstPt = new Point2D.Float();
|
||||
}
|
||||
|
||||
// Copy location
|
||||
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
|
||||
|
||||
// Return dest
|
||||
return pDstPt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendering mHints for this op.
|
||||
* @return the {@code RenderingHints} object associated
|
||||
* with this op.
|
||||
*/
|
||||
public final RenderingHints getRenderingHints() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int ARGB to int triplet.
|
||||
*/
|
||||
private static int[] toRGBArray(int pARGB, int[] pBuffer) {
|
||||
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
|
||||
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
|
||||
pBuffer[2] = ((pARGB & 0x000000ff));
|
||||
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a int triplet to int ARGB.
|
||||
*/
|
||||
private static int toIntARGB(int[] pRGB) {
|
||||
return 0xff000000 // All opaque
|
||||
| (pRGB[0] << 16)
|
||||
| (pRGB[1] << 8)
|
||||
| (pRGB[2]);
|
||||
/*
|
||||
| ((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.
|
||||
*
|
||||
* @param pSource the source image
|
||||
* @param pDest the destination image
|
||||
*
|
||||
* @return the destination image, or a new image, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||
// Create destination image, if none provided
|
||||
if (pDest == null) {
|
||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||
}
|
||||
else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
|
||||
// Filter rasters
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
* {@code null}.
|
||||
*/
|
||||
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(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);
|
||||
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.
|
||||
*
|
||||
* @param pSource the source raster, assumed to be in sRGB
|
||||
* @param pDest the destination raster, may be {@code null}
|
||||
* @param pColorModel the indexed color model to use
|
||||
*
|
||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||
int width = pSource.getWidth();
|
||||
int height = pSource.getHeight();
|
||||
|
||||
// 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
|
||||
// offset from actual column position by one to allow FS to not check
|
||||
// left/right edge conditions
|
||||
int[][] currErr = new int[width + 2][3];
|
||||
int[][] nextErr = new int[width + 2][3];
|
||||
|
||||
// Random errors in [-1 .. 1] - for first row
|
||||
for (int i = 0; i < width + 2; i++) {
|
||||
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
|
||||
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];
|
||||
final int[] outRGB = new int[4];
|
||||
Object pixel = null;
|
||||
boolean forward = true;
|
||||
|
||||
// Loop through image data
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Clear out next error rows for colour errors
|
||||
for (int i = nextErr.length; --i >= 0;) {
|
||||
nextErr[i][0] = 0;
|
||||
nextErr[i][1] = 0;
|
||||
nextErr[i][2] = 0;
|
||||
}
|
||||
|
||||
// Set up start column and limit
|
||||
int x;
|
||||
int limit;
|
||||
if (forward) {
|
||||
x = 0;
|
||||
limit = width;
|
||||
}
|
||||
else {
|
||||
x = width - 1;
|
||||
limit = -1;
|
||||
}
|
||||
|
||||
// TODO: Use getPixels instead of getPixel for better performance?
|
||||
|
||||
// Loop over row
|
||||
while (true) {
|
||||
// Get RGB from original raster
|
||||
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
|
||||
pSource.getPixel(x, y, inRGB);
|
||||
|
||||
// Get error for this pixel & add error to rgb
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Make a 28.4 FP number, add Error (with fraction),
|
||||
// rounding and truncate to int
|
||||
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
|
||||
|
||||
// Clamp
|
||||
if (inRGB[i] > 255) {
|
||||
inRGB[i] = 255;
|
||||
}
|
||||
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.
|
||||
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
|
||||
|
||||
// ...set it...
|
||||
pDest.setDataElements(x, y, pixel);
|
||||
|
||||
// ..and get back the closet match
|
||||
pDest.getPixel(x, y, outRGB);
|
||||
|
||||
// Convert the value to default sRGB
|
||||
// Should work for all transfertypes supported by IndexColorModel
|
||||
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
|
||||
|
||||
// Find diff
|
||||
diff[0] = inRGB[0] - outRGB[0];
|
||||
diff[1] = inRGB[1] - outRGB[1];
|
||||
diff[2] = inRGB[2] - outRGB[2];
|
||||
|
||||
// Apply F-S error diffusion
|
||||
// Serpentine scan: left-right
|
||||
if (forward) {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x + 1)
|
||||
currErr[x + 2][0] += diff[0] * 7;
|
||||
currErr[x + 2][1] += diff[1] * 7;
|
||||
currErr[x + 2][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x - 1)
|
||||
nextErr[x][0] += diff[0] * 3;
|
||||
nextErr[x][1] += diff[1] * 3;
|
||||
nextErr[x][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x + 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x + 2][0] += diff[0]; // * 1;
|
||||
nextErr[x + 2][1] += diff[1]; // * 1;
|
||||
nextErr[x + 2][2] += diff[2]; // * 1;
|
||||
|
||||
// Next
|
||||
x++;
|
||||
|
||||
// Done?
|
||||
if (x >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x - 1)
|
||||
currErr[x][0] += diff[0] * 7;
|
||||
currErr[x][1] += diff[1] * 7;
|
||||
currErr[x][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x + 1)
|
||||
nextErr[x + 2][0] += diff[0] * 3;
|
||||
nextErr[x + 2][1] += diff[1] * 3;
|
||||
nextErr[x + 2][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x - 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x][0] += diff[0]; // * 1;
|
||||
nextErr[x][1] += diff[1]; // * 1;
|
||||
nextErr[x][2] += diff[2]; // * 1;
|
||||
|
||||
// Previous
|
||||
x--;
|
||||
|
||||
// Done?
|
||||
if (x <= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make next error info current for next iteration
|
||||
int[][] temperr;
|
||||
temperr = currErr;
|
||||
currErr = nextErr;
|
||||
nextErr = temperr;
|
||||
|
||||
// Toggle direction
|
||||
if (alternateScans) {
|
||||
forward = !forward;
|
||||
}
|
||||
}
|
||||
|
||||
return pDest;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* GraphicsUtil
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.no">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/GraphicsUtil.java#1 $
|
||||
*/
|
||||
public final class GraphicsUtil {
|
||||
|
||||
/**
|
||||
* Enables anti-aliasing in the {@code Graphics} object.
|
||||
* <p/>
|
||||
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
||||
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
||||
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.RenderingHints#KEY_ANTIALIASING
|
||||
*/
|
||||
public static void enableAA(final Graphics pGraphics) {
|
||||
((Graphics2D) pGraphics).setRenderingHint(
|
||||
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha in the {@code Graphics} object.
|
||||
* <p/>
|
||||
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
||||
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
||||
* alpha.
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
||||
* number in the inclusive range [0.0, 1.0].
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.AlphaComposite#SRC_OVER
|
||||
* @see java.awt.AlphaComposite#getInstance(int, float)
|
||||
*/
|
||||
public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
|
||||
((Graphics2D) pGraphics).setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
|
||||
);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* GraphicsUtil
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.no">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/GraphicsUtil.java#1 $
|
||||
*/
|
||||
public final class GraphicsUtil {
|
||||
|
||||
/**
|
||||
* Enables anti-aliasing in the {@code Graphics} object.
|
||||
* <p>
|
||||
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
||||
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
||||
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
||||
* </p>
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.RenderingHints#KEY_ANTIALIASING
|
||||
*/
|
||||
public static void enableAA(final Graphics pGraphics) {
|
||||
((Graphics2D) pGraphics).setRenderingHint(
|
||||
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha in the {@code Graphics} object.
|
||||
* <p>
|
||||
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
||||
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
||||
* alpha.
|
||||
* </p>
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
||||
* number in the inclusive range [0.0, 1.0].
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.AlphaComposite#SRC_OVER
|
||||
* @see java.awt.AlphaComposite#getInstance(int, float)
|
||||
*/
|
||||
public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
|
||||
((Graphics2D) pGraphics).setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,131 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
/**
|
||||
* This class can convert a color image to grayscale.
|
||||
* <P/>
|
||||
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
||||
*
|
||||
* @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 $
|
||||
*
|
||||
*/
|
||||
public class GrayFilter extends RGBImageFilter {
|
||||
|
||||
// This filter can filter IndexColorModel
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
private int low = 0;
|
||||
private float range = 1.0f;
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion.
|
||||
*/
|
||||
public GrayFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public GrayFilter(float pLow, float pHigh) {
|
||||
if (pLow > pHigh) {
|
||||
pLow = 0f;
|
||||
}
|
||||
// Make sure high and low are inside range
|
||||
if (pLow < 0f) {
|
||||
pLow = 0f;
|
||||
}
|
||||
else if (pLow > 1f) {
|
||||
pLow = 1f;
|
||||
}
|
||||
if (pHigh < 0f) {
|
||||
pHigh = 0f;
|
||||
}
|
||||
else if (pHigh > 1f) {
|
||||
pHigh = 1f;
|
||||
}
|
||||
|
||||
low = (int) (pLow * 255f);
|
||||
range = pHigh - pLow;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public GrayFilter(int pLow, int pHigh) {
|
||||
this(pLow / 255f, pHigh / 255f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel using ITU color-conversion.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in 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
|
||||
int r = pARGB >> 16 & 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;
|
||||
|
||||
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
||||
|
||||
if (range != 1.0f) {
|
||||
// Apply range
|
||||
gray = low + (int) (gray * range);
|
||||
}
|
||||
|
||||
// Return ARGB pixel
|
||||
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
/**
|
||||
* This class can convert a color image to grayscale.
|
||||
* <p>
|
||||
* 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 $
|
||||
*
|
||||
* @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 {
|
||||
|
||||
// This filter can filter IndexColorModel
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
private int low = 0;
|
||||
private float range = 1.0f;
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion.
|
||||
*/
|
||||
public GrayFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public GrayFilter(float pLow, float pHigh) {
|
||||
if (pLow > pHigh) {
|
||||
pLow = 0f;
|
||||
}
|
||||
// Make sure high and low are inside range
|
||||
if (pLow < 0f) {
|
||||
pLow = 0f;
|
||||
}
|
||||
else if (pLow > 1f) {
|
||||
pLow = 1f;
|
||||
}
|
||||
if (pHigh < 0f) {
|
||||
pHigh = 0f;
|
||||
}
|
||||
else if (pHigh > 1f) {
|
||||
pHigh = 1f;
|
||||
}
|
||||
|
||||
low = (int) (pLow * 255f);
|
||||
range = pHigh - pLow;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 integer in the range 0..255 and >= pLow
|
||||
*/
|
||||
public GrayFilter(int pLow, int pHigh) {
|
||||
this(pLow / 255f, pHigh / 255f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel using ITU color-conversion.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in 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
|
||||
int r = pARGB >> 16 & 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;
|
||||
|
||||
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
||||
|
||||
if (range != 1.0f) {
|
||||
// Apply range
|
||||
gray = low + (int) (gray * range);
|
||||
}
|
||||
|
||||
// Return ARGB pixel
|
||||
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,213 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
/**
|
||||
* Inverse Colormap to provide efficient lookup of any given input color
|
||||
* to the closest match to the given color map.
|
||||
* <p/>
|
||||
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
||||
* in "Graphics Gems Volume II"
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @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 $
|
||||
*/
|
||||
class InverseColorMap {
|
||||
/**
|
||||
* Number of high bits of each color channel to use to lookup near match
|
||||
*/
|
||||
final static int QUANTBITS = 5;
|
||||
|
||||
/**
|
||||
* Truncated bits of each color channel
|
||||
*/
|
||||
final static int TRUNCBITS = 8 - QUANTBITS;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for blue in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_BLUE = (1 << 5) - 1;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for green in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for red in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
|
||||
|
||||
/**
|
||||
* Maximum value a quantised color channel can have
|
||||
*/
|
||||
final static int MAXQUANTVAL = 1 << 5;
|
||||
|
||||
byte[] rgbMapByte;
|
||||
int[] rgbMapInt;
|
||||
int numColors;
|
||||
int maxColor;
|
||||
byte[] inverseRGB; // inverse rgb color map
|
||||
int transparentIndex = -1;
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap) {
|
||||
this(pRGBColorMap, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
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
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapByte = pRGBColorMap;
|
||||
numColors = rgbMapByte.length / 4;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
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
|
||||
*/
|
||||
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapInt = pRGBColorMap;
|
||||
numColors = rgbMapInt.length;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
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) {
|
||||
// Skip the transparent pixel
|
||||
continue;
|
||||
}
|
||||
|
||||
int red, r, rdist, rinc, rxx;
|
||||
int green, g, gdist, ginc, gxx;
|
||||
int blue, b, bdist, binc, bxx;
|
||||
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
if (rgbMapByte != null) {
|
||||
red = rgbMapByte[i * 4] & 0xFF;
|
||||
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
||||
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
||||
}
|
||||
else if (rgbMapInt != null) {
|
||||
red = (rgbMapInt[i] >> 16) & 0xFF;
|
||||
green = (rgbMapInt[i] >> 8) & 0xFF;
|
||||
blue = rgbMapInt[i] & 0xFF;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("colormap == null");
|
||||
}
|
||||
|
||||
rdist = red - x / 2; // distance of red to center of current cell
|
||||
gdist = green - x / 2; // green
|
||||
bdist = blue - x / 2; // blue
|
||||
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
|
||||
|
||||
rinc = 2 * (xsqr - (red << 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) {
|
||||
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
|
||||
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
||||
if (i == 0 || pTemp[rgbI] > bdist) {
|
||||
pTemp[rgbI] = bdist;
|
||||
inverseRGB[rgbI] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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) +
|
||||
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pBlue blue 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
|
||||
* 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.
|
||||
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
||||
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
/**
|
||||
* Inverse Colormap to provide efficient lookup of any given input color
|
||||
* to the closest match to the given color map.
|
||||
* <p>
|
||||
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
||||
* in "Graphics Gems Volume II".
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @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 $
|
||||
*/
|
||||
class InverseColorMap {
|
||||
/**
|
||||
* Number of high bits of each color channel to use to lookup near match
|
||||
*/
|
||||
final static int QUANTBITS = 5;
|
||||
|
||||
/**
|
||||
* Truncated bits of each color channel
|
||||
*/
|
||||
final static int TRUNCBITS = 8 - QUANTBITS;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for blue in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_BLUE = (1 << 5) - 1;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for green in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for red in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
|
||||
|
||||
/**
|
||||
* Maximum value a quantised color channel can have
|
||||
*/
|
||||
final static int MAXQUANTVAL = 1 << 5;
|
||||
|
||||
byte[] rgbMapByte;
|
||||
int[] rgbMapInt;
|
||||
int numColors;
|
||||
int maxColor;
|
||||
byte[] inverseRGB; // inverse rgb color map
|
||||
int transparentIndex = -1;
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap) {
|
||||
this(pRGBColorMap, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
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
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapByte = pRGBColorMap;
|
||||
numColors = rgbMapByte.length / 4;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
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
|
||||
*/
|
||||
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapInt = pRGBColorMap;
|
||||
numColors = rgbMapInt.length;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
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) {
|
||||
// Skip the transparent pixel
|
||||
continue;
|
||||
}
|
||||
|
||||
int red, r, rdist, rinc, rxx;
|
||||
int green, g, gdist, ginc, gxx;
|
||||
int blue, b, bdist, binc, bxx;
|
||||
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
if (rgbMapByte != null) {
|
||||
red = rgbMapByte[i * 4] & 0xFF;
|
||||
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
||||
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
||||
}
|
||||
else if (rgbMapInt != null) {
|
||||
red = (rgbMapInt[i] >> 16) & 0xFF;
|
||||
green = (rgbMapInt[i] >> 8) & 0xFF;
|
||||
blue = rgbMapInt[i] & 0xFF;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("colormap == null");
|
||||
}
|
||||
|
||||
rdist = red - x / 2; // distance of red to center of current cell
|
||||
gdist = green - x / 2; // green
|
||||
bdist = blue - x / 2; // blue
|
||||
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
|
||||
|
||||
rinc = 2 * (xsqr - (red << 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) {
|
||||
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
|
||||
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
||||
if (i == 0 || pTemp[rgbI] > bdist) {
|
||||
pTemp[rgbI] = bdist;
|
||||
inverseRGB[rgbI] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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) +
|
||||
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pBlue blue 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
|
||||
* 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.
|
||||
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
||||
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,187 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import magick.MagickImage;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
|
||||
/**
|
||||
* This class accelerates certain graphics operations, using
|
||||
* JMagick and ImageMagick, if available.
|
||||
* If those libraries are not installed, this class silently does nothing.
|
||||
* <p/>
|
||||
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
|
||||
* {@code false}, to disable, even if JMagick is installed.
|
||||
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
|
||||
* <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/image/MagickAccelerator.java#3 $
|
||||
*/
|
||||
final class MagickAccelerator {
|
||||
|
||||
private static final boolean DEBUG = Magick.DEBUG;
|
||||
private static final boolean USE_MAGICK = useMagick();
|
||||
|
||||
private static final int RESAMPLE_OP = 0;
|
||||
|
||||
private static Class[] nativeOp = new Class[1];
|
||||
|
||||
static {
|
||||
try {
|
||||
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
System.err.println("Could not find class: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useMagick() {
|
||||
try {
|
||||
boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
|
||||
|
||||
if (DEBUG && !available) {
|
||||
System.err.print("ImageMagick bindings not available.");
|
||||
}
|
||||
|
||||
boolean useMagick =
|
||||
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println(
|
||||
useMagick
|
||||
? "Will use ImageMagick bindings to accelerate image resampling operations."
|
||||
: "Will not use ImageMagick to accelerate image resampling operations."
|
||||
);
|
||||
}
|
||||
|
||||
return useMagick;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Most probably in case of a SecurityManager
|
||||
System.err.println("Could not enable ImageMagick bindings: " + t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNativeOpIndex(Class pOpClass) {
|
||||
for (int i = 0; i < nativeOp.length; i++) {
|
||||
if (pOpClass == nativeOp[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
|
||||
if (!USE_MAGICK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage result = null;
|
||||
switch (getNativeOpIndex(pOperation.getClass())) {
|
||||
case RESAMPLE_OP:
|
||||
ResampleOp resample = (ResampleOp) pOperation;
|
||||
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
|
||||
|
||||
// NOTE: If output parameter is non-null, we have to return that
|
||||
// image, instead of result
|
||||
if (pOutput != null) {
|
||||
//pOutput.setData(result.getRaster()); // Fast, but less compatible
|
||||
// NOTE: For some reason, this is sometimes super-slow...?
|
||||
ImageUtil.drawOnto(pOutput, result);
|
||||
result = pOutput;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Simply fall through, allowing acceleration to be added later
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
|
||||
// Convert to Magick, scale and convert back
|
||||
MagickImage image = null;
|
||||
MagickImage scaled = null;
|
||||
try {
|
||||
image = MagickUtil.toMagick(pSrc);
|
||||
|
||||
long start = 0;
|
||||
if (DEBUG) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// NOTE: setFilter affects zoomImage, NOT scaleImage
|
||||
image.setFilter(pFilterType);
|
||||
scaled = image.zoomImage(pWidth, pHeight);
|
||||
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
|
||||
|
||||
if (DEBUG) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
System.out.println("Filtered: " + time + " ms");
|
||||
}
|
||||
|
||||
return MagickUtil.toBuffered(scaled);
|
||||
}
|
||||
//catch (MagickException e) {
|
||||
catch (Exception e) {
|
||||
// NOTE: Stupid workaround: If MagickException is caught, a
|
||||
// NoClassDefFoundError is thrown, when MagickException class is
|
||||
// unavailable...
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
|
||||
throw new ImageConversionException(e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
// NOTE: ImageMagick might be unstable after a while, if image data
|
||||
// is not deallocated. The GC/finalize method handles this, but in
|
||||
// special circumstances, it's not triggered often enough.
|
||||
if (image != null) {
|
||||
image.destroyImages();
|
||||
}
|
||||
if (scaled != null) {
|
||||
scaled.destroyImages();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import magick.MagickImage;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
|
||||
/**
|
||||
* This class accelerates certain graphics operations, using
|
||||
* JMagick and ImageMagick, if available.
|
||||
* If those libraries are not installed, this class silently does nothing.
|
||||
* <p>
|
||||
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
|
||||
* {@code false}, to disable, even if JMagick is installed.
|
||||
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
|
||||
* </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/image/MagickAccelerator.java#3 $
|
||||
*/
|
||||
final class MagickAccelerator {
|
||||
|
||||
private static final boolean DEBUG = Magick.DEBUG;
|
||||
private static final boolean USE_MAGICK = useMagick();
|
||||
|
||||
private static final int RESAMPLE_OP = 0;
|
||||
|
||||
private static Class[] nativeOp = new Class[1];
|
||||
|
||||
static {
|
||||
try {
|
||||
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
System.err.println("Could not find class: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useMagick() {
|
||||
try {
|
||||
boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
|
||||
|
||||
if (DEBUG && !available) {
|
||||
System.err.print("ImageMagick bindings not available.");
|
||||
}
|
||||
|
||||
boolean useMagick =
|
||||
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println(
|
||||
useMagick
|
||||
? "Will use ImageMagick bindings to accelerate image resampling operations."
|
||||
: "Will not use ImageMagick to accelerate image resampling operations."
|
||||
);
|
||||
}
|
||||
|
||||
return useMagick;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Most probably in case of a SecurityManager
|
||||
System.err.println("Could not enable ImageMagick bindings: " + t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNativeOpIndex(Class pOpClass) {
|
||||
for (int i = 0; i < nativeOp.length; i++) {
|
||||
if (pOpClass == nativeOp[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
|
||||
if (!USE_MAGICK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage result = null;
|
||||
switch (getNativeOpIndex(pOperation.getClass())) {
|
||||
case RESAMPLE_OP:
|
||||
ResampleOp resample = (ResampleOp) pOperation;
|
||||
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
|
||||
|
||||
// NOTE: If output parameter is non-null, we have to return that
|
||||
// image, instead of result
|
||||
if (pOutput != null) {
|
||||
//pOutput.setData(result.getRaster()); // Fast, but less compatible
|
||||
// NOTE: For some reason, this is sometimes super-slow...?
|
||||
ImageUtil.drawOnto(pOutput, result);
|
||||
result = pOutput;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Simply fall through, allowing acceleration to be added later
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
|
||||
// Convert to Magick, scale and convert back
|
||||
MagickImage image = null;
|
||||
MagickImage scaled = null;
|
||||
try {
|
||||
image = MagickUtil.toMagick(pSrc);
|
||||
|
||||
long start = 0;
|
||||
if (DEBUG) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// NOTE: setFilter affects zoomImage, NOT scaleImage
|
||||
image.setFilter(pFilterType);
|
||||
scaled = image.zoomImage(pWidth, pHeight);
|
||||
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
|
||||
|
||||
if (DEBUG) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
System.out.println("Filtered: " + time + " ms");
|
||||
}
|
||||
|
||||
return MagickUtil.toBuffered(scaled);
|
||||
}
|
||||
//catch (MagickException e) {
|
||||
catch (Exception e) {
|
||||
// NOTE: Stupid workaround: If MagickException is caught, a
|
||||
// NoClassDefFoundError is thrown, when MagickException class is
|
||||
// unavailable...
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
|
||||
throw new ImageConversionException(e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
// NOTE: ImageMagick might be unstable after a while, if image data
|
||||
// is not deallocated. The GC/finalize method handles this, but in
|
||||
// special circumstances, it's not triggered often enough.
|
||||
if (image != null) {
|
||||
image.destroyImages();
|
||||
}
|
||||
if (scaled != null) {
|
||||
scaled.destroyImages();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,79 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ReplicateScaleFilter;
|
||||
|
||||
/**
|
||||
* An {@code ImageFilter} class for subsampling images.
|
||||
* <p/>
|
||||
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
||||
* object to produce subsampled versions of existing images.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class SubsamplingFilter extends ReplicateScaleFilter {
|
||||
private int xSub;
|
||||
private int ySub;
|
||||
|
||||
/**
|
||||
* Creates a {@code SubsamplingFilter}.
|
||||
*
|
||||
* @param pXSub
|
||||
* @param pYSub
|
||||
*
|
||||
* @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
|
||||
// until w/h is available, in setDimensions below
|
||||
|
||||
if (pXSub < 1 || pYSub < 1) {
|
||||
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
||||
}
|
||||
|
||||
xSub = pXSub;
|
||||
ySub = pYSub;
|
||||
}
|
||||
|
||||
/** {@code ImageFilter} implementation, do not invoke. */
|
||||
public void setDimensions(int pWidth, int pHeight) {
|
||||
destWidth = (pWidth + xSub - 1) / xSub;
|
||||
destHeight = (pHeight + ySub - 1) / ySub;
|
||||
|
||||
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
||||
super.setDimensions(pWidth, pHeight);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ReplicateScaleFilter;
|
||||
|
||||
/**
|
||||
* An {@code ImageFilter} class for subsampling images.
|
||||
* <p>
|
||||
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
||||
* object to produce subsampled versions of existing images.
|
||||
* </p>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class SubsamplingFilter extends ReplicateScaleFilter {
|
||||
private int xSub;
|
||||
private int ySub;
|
||||
|
||||
/**
|
||||
* Creates a {@code SubsamplingFilter}.
|
||||
*
|
||||
* @param pXSub
|
||||
* @param pYSub
|
||||
*
|
||||
* @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
|
||||
// until w/h is available, in setDimensions below
|
||||
|
||||
if (pXSub < 1 || pYSub < 1) {
|
||||
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
||||
}
|
||||
|
||||
xSub = pXSub;
|
||||
ySub = pYSub;
|
||||
}
|
||||
|
||||
/** {@code ImageFilter} implementation, do not invoke. */
|
||||
public void setDimensions(int pWidth, int pHeight) {
|
||||
destWidth = (pWidth + xSub - 1) / xSub;
|
||||
destHeight = (pHeight + ySub - 1) / ySub;
|
||||
|
||||
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
||||
super.setDimensions(pWidth, pHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
|
||||
/**
|
||||
* Classes for image manipulation.
|
||||
* <p/>
|
||||
* <p>
|
||||
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
||||
* </p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
|
||||
@@ -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
|
||||
* {@code pLength}.
|
||||
* <p/>
|
||||
* <p>
|
||||
* This implementation invokes {@link #write(int)} {@code pLength} times.
|
||||
* Subclasses may override this method for performance.
|
||||
* </p>
|
||||
*
|
||||
* @param pBuffer the bytes to write.
|
||||
* @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
|
||||
* {@code pLength}.
|
||||
* <p/>
|
||||
* <p>
|
||||
* This implementation invokes {@link #read()} {@code pLength} times.
|
||||
* Subclasses may override this method for performance.
|
||||
* </p>
|
||||
*
|
||||
* @param pBuffer the bytes to write
|
||||
* @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.
|
||||
* <p/>
|
||||
* <p>
|
||||
* 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.
|
||||
* <p/>
|
||||
* </p>
|
||||
* <p>
|
||||
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
|
||||
* This implementation does nothing.
|
||||
* </p>
|
||||
*
|
||||
* @param pPosition the last position to flush.
|
||||
*/
|
||||
|
||||
@@ -1,222 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Reader implementation that can read from multiple sources.
|
||||
* <p/>
|
||||
*
|
||||
* @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/io/CompoundReader.java#2 $
|
||||
*/
|
||||
public class CompoundReader extends Reader {
|
||||
|
||||
private Reader current;
|
||||
private List<Reader> readers;
|
||||
|
||||
protected final Object finalLock;
|
||||
|
||||
protected final boolean markSupported;
|
||||
|
||||
private int currentReader;
|
||||
private int markedReader;
|
||||
private int mark;
|
||||
private int mNext;
|
||||
|
||||
/**
|
||||
* Create a new compound reader.
|
||||
*
|
||||
* @param pReaders {@code Iterator} containting {@code Reader}s,
|
||||
* providing the character stream.
|
||||
*
|
||||
* @throws NullPointerException if {@code pReaders} is {@code null}, or
|
||||
* any of the elements in the iterator is {@code null}.
|
||||
* @throws ClassCastException if any element of the iterator is not a
|
||||
* {@code java.io.Reader}
|
||||
*/
|
||||
public CompoundReader(final Iterator<Reader> pReaders) {
|
||||
super(Validate.notNull(pReaders, "readers"));
|
||||
|
||||
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
|
||||
// reference can't change, only it's elements
|
||||
|
||||
readers = new ArrayList<Reader>();
|
||||
|
||||
boolean markSupported = true;
|
||||
while (pReaders.hasNext()) {
|
||||
Reader reader = pReaders.next();
|
||||
if (reader == null) {
|
||||
throw new NullPointerException("readers cannot contain null-elements");
|
||||
}
|
||||
readers.add(reader);
|
||||
markSupported = markSupported && reader.markSupported();
|
||||
}
|
||||
this.markSupported = markSupported;
|
||||
|
||||
current = nextReader();
|
||||
}
|
||||
|
||||
protected final Reader nextReader() {
|
||||
if (currentReader >= readers.size()) {
|
||||
current = new EmptyReader();
|
||||
}
|
||||
else {
|
||||
current = readers.get(currentReader++);
|
||||
}
|
||||
|
||||
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
||||
mNext = 0;
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to make sure that the stream has not been closed
|
||||
*
|
||||
* @throws IOException if the stream is closed
|
||||
*/
|
||||
protected final void ensureOpen() throws IOException {
|
||||
if (readers == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
// Close all readers
|
||||
for (Reader reader : readers) {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
readers = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int pReadLimit) throws IOException {
|
||||
if (pReadLimit < 0) {
|
||||
throw new IllegalArgumentException("Read limit < 0");
|
||||
}
|
||||
|
||||
// TODO: It would be nice if we could actually close some readers now
|
||||
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
mark = mNext;
|
||||
markedReader = currentReader;
|
||||
|
||||
current.mark(pReadLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
|
||||
if (currentReader != markedReader) {
|
||||
// Reset any reader before this
|
||||
for (int i = currentReader; i >= markedReader; i--) {
|
||||
readers.get(i).reset();
|
||||
}
|
||||
|
||||
currentReader = markedReader - 1;
|
||||
nextReader();
|
||||
}
|
||||
current.reset();
|
||||
|
||||
mNext = mark;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return markSupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read();
|
||||
|
||||
if (read < 0 && currentReader < readers.size()) {
|
||||
nextReader();
|
||||
return read(); // In case of 0-length readers
|
||||
}
|
||||
|
||||
mNext++;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read(pBuffer, pOffset, pLength);
|
||||
|
||||
if (read < 0 && currentReader < readers.size()) {
|
||||
nextReader();
|
||||
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
|
||||
}
|
||||
|
||||
mNext += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ready() throws IOException {
|
||||
return current.ready();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long pChars) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
long skipped = current.skip(pChars);
|
||||
|
||||
if (skipped == 0 && currentReader < readers.size()) {
|
||||
nextReader();
|
||||
return skip(pChars); // In case of 0-length readers
|
||||
}
|
||||
|
||||
mNext += skipped;
|
||||
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Reader implementation that can read from multiple sources.
|
||||
*
|
||||
* @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/io/CompoundReader.java#2 $
|
||||
*/
|
||||
public class CompoundReader extends Reader {
|
||||
|
||||
private Reader current;
|
||||
private List<Reader> readers;
|
||||
|
||||
protected final Object finalLock;
|
||||
|
||||
protected final boolean markSupported;
|
||||
|
||||
private int currentReader;
|
||||
private int markedReader;
|
||||
private int mark;
|
||||
private int mNext;
|
||||
|
||||
/**
|
||||
* Create a new compound reader.
|
||||
*
|
||||
* @param pReaders {@code Iterator} containting {@code Reader}s,
|
||||
* providing the character stream.
|
||||
*
|
||||
* @throws NullPointerException if {@code pReaders} is {@code null}, or
|
||||
* any of the elements in the iterator is {@code null}.
|
||||
* @throws ClassCastException if any element of the iterator is not a
|
||||
* {@code java.io.Reader}
|
||||
*/
|
||||
public CompoundReader(final Iterator<Reader> pReaders) {
|
||||
super(Validate.notNull(pReaders, "readers"));
|
||||
|
||||
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
|
||||
// reference can't change, only it's elements
|
||||
|
||||
readers = new ArrayList<Reader>();
|
||||
|
||||
boolean markSupported = true;
|
||||
while (pReaders.hasNext()) {
|
||||
Reader reader = pReaders.next();
|
||||
if (reader == null) {
|
||||
throw new NullPointerException("readers cannot contain null-elements");
|
||||
}
|
||||
readers.add(reader);
|
||||
markSupported = markSupported && reader.markSupported();
|
||||
}
|
||||
this.markSupported = markSupported;
|
||||
|
||||
current = nextReader();
|
||||
}
|
||||
|
||||
protected final Reader nextReader() {
|
||||
if (currentReader >= readers.size()) {
|
||||
current = new EmptyReader();
|
||||
}
|
||||
else {
|
||||
current = readers.get(currentReader++);
|
||||
}
|
||||
|
||||
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
||||
mNext = 0;
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to make sure that the stream has not been closed
|
||||
*
|
||||
* @throws IOException if the stream is closed
|
||||
*/
|
||||
protected final void ensureOpen() throws IOException {
|
||||
if (readers == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
// Close all readers
|
||||
for (Reader reader : readers) {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
readers = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int pReadLimit) throws IOException {
|
||||
if (pReadLimit < 0) {
|
||||
throw new IllegalArgumentException("Read limit < 0");
|
||||
}
|
||||
|
||||
// TODO: It would be nice if we could actually close some readers now
|
||||
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
mark = mNext;
|
||||
markedReader = currentReader;
|
||||
|
||||
current.mark(pReadLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
|
||||
if (currentReader != markedReader) {
|
||||
// Reset any reader before this
|
||||
for (int i = currentReader; i >= markedReader; i--) {
|
||||
readers.get(i).reset();
|
||||
}
|
||||
|
||||
currentReader = markedReader - 1;
|
||||
nextReader();
|
||||
}
|
||||
current.reset();
|
||||
|
||||
mNext = mark;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return markSupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read();
|
||||
|
||||
if (read < 0 && currentReader < readers.size()) {
|
||||
nextReader();
|
||||
return read(); // In case of 0-length readers
|
||||
}
|
||||
|
||||
mNext++;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read(pBuffer, pOffset, pLength);
|
||||
|
||||
if (read < 0 && currentReader < readers.size()) {
|
||||
nextReader();
|
||||
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
|
||||
}
|
||||
|
||||
mNext += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ready() throws IOException {
|
||||
return current.ready();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long pChars) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
long skipped = current.skip(pChars);
|
||||
|
||||
if (skipped == 0 && currentReader < readers.size()) {
|
||||
nextReader();
|
||||
return skip(pChars); // In case of 0-length readers
|
||||
}
|
||||
|
||||
mNext += skipped;
|
||||
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* EmptyReader
|
||||
* <p/>
|
||||
*
|
||||
* @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/io/EmptyReader.java#1 $
|
||||
*/
|
||||
final class EmptyReader extends StringReader {
|
||||
public EmptyReader() {
|
||||
super("");
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* EmptyReader
|
||||
*
|
||||
* @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/io/EmptyReader.java#1 $
|
||||
*/
|
||||
final class EmptyReader extends StringReader {
|
||||
public EmptyReader() {
|
||||
super("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,136 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
||||
* 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>
|
||||
* @version $Id: FastByteArrayOutputStream.java#2 $
|
||||
*/
|
||||
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
||||
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
/** Max grow size (unless if writing more than this amount of bytes) */
|
||||
protected int maxGrowSize = 1024 * 1024; // 1 MB
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
||||
* size.
|
||||
*
|
||||
* @param pSize initial buffer size
|
||||
*/
|
||||
public FastByteArrayOutputStream(int pSize) {
|
||||
super(pSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayOutputStream} with the given initial content.
|
||||
* <p/>
|
||||
* Note that the buffer is not cloned, for maximum performance.
|
||||
*
|
||||
* @param pBuffer initial buffer
|
||||
*/
|
||||
public FastByteArrayOutputStream(byte[] pBuffer) {
|
||||
super(0); // Don't allocate array
|
||||
buf = pBuffer;
|
||||
count = pBuffer.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte pBytes[], int pOffset, int pLength) {
|
||||
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newCount = count + pLength;
|
||||
growIfNeeded(newCount);
|
||||
System.arraycopy(pBytes, pOffset, buf, count, pLength);
|
||||
count = newCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int pByte) {
|
||||
int newCount = count + 1;
|
||||
growIfNeeded(newCount);
|
||||
buf[count] = (byte) pByte;
|
||||
count = newCount;
|
||||
}
|
||||
|
||||
private void growIfNeeded(int pNewCount) {
|
||||
if (pNewCount > buf.length) {
|
||||
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
||||
byte newBuf[] = new byte[newSize];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
buf = newBuf;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-synchronized version of writeTo
|
||||
@Override
|
||||
public void writeTo(OutputStream pOut) throws IOException {
|
||||
pOut.write(buf, 0, count);
|
||||
}
|
||||
|
||||
// Non-synchronized version of toByteArray
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
byte newBuf[] = new byte[count];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayInputStream} that reads directly from this
|
||||
* {@code FastByteArrayOutputStream}'s byte buffer.
|
||||
* The buffer is not cloned, for maximum performance.
|
||||
* <p/>
|
||||
* Note that care needs to be taken to avoid writes to
|
||||
* this output stream after the input stream is created.
|
||||
* Failing to do so, may result in unpredictable behaviour.
|
||||
*
|
||||
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
||||
*/
|
||||
public ByteArrayInputStream createInputStream() {
|
||||
return new ByteArrayInputStream(buf, 0, count);
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
||||
* also has a constructor that lets you create a stream with initial content.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: FastByteArrayOutputStream.java#2 $
|
||||
*/
|
||||
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
||||
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
/** Max grow size (unless if writing more than this amount of bytes) */
|
||||
protected int maxGrowSize = 1024 * 1024; // 1 MB
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
||||
* size.
|
||||
*
|
||||
* @param pSize initial buffer size
|
||||
*/
|
||||
public FastByteArrayOutputStream(int pSize) {
|
||||
super(pSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayOutputStream} with the given initial content.
|
||||
* <p>
|
||||
* Note that the buffer is not cloned, for maximum performance.
|
||||
* </p>
|
||||
*
|
||||
* @param pBuffer initial buffer
|
||||
*/
|
||||
public FastByteArrayOutputStream(byte[] pBuffer) {
|
||||
super(0); // Don't allocate array
|
||||
buf = pBuffer;
|
||||
count = pBuffer.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte pBytes[], int pOffset, int pLength) {
|
||||
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newCount = count + pLength;
|
||||
growIfNeeded(newCount);
|
||||
System.arraycopy(pBytes, pOffset, buf, count, pLength);
|
||||
count = newCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int pByte) {
|
||||
int newCount = count + 1;
|
||||
growIfNeeded(newCount);
|
||||
buf[count] = (byte) pByte;
|
||||
count = newCount;
|
||||
}
|
||||
|
||||
private void growIfNeeded(int pNewCount) {
|
||||
if (pNewCount > buf.length) {
|
||||
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
||||
byte newBuf[] = new byte[newSize];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
buf = newBuf;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-synchronized version of writeTo
|
||||
@Override
|
||||
public void writeTo(OutputStream pOut) throws IOException {
|
||||
pOut.write(buf, 0, count);
|
||||
}
|
||||
|
||||
// Non-synchronized version of toByteArray
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
byte newBuf[] = new byte[count];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayInputStream} that reads directly from this
|
||||
* {@code FastByteArrayOutputStream}'s byte buffer.
|
||||
* The buffer is not cloned, for maximum performance.
|
||||
* <p>
|
||||
* Note that care needs to be taken to avoid writes to
|
||||
* this output stream after the input stream is created.
|
||||
* Failing to do so, may result in unpredictable behaviour.
|
||||
* </p>
|
||||
*
|
||||
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
||||
*/
|
||||
public ByteArrayInputStream createInputStream() {
|
||||
return new ByteArrayInputStream(buf, 0, count);
|
||||
}
|
||||
}
|
||||
@@ -1,240 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
|
||||
* <p/>
|
||||
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
|
||||
*
|
||||
* @see MemoryCacheSeekableStream
|
||||
* @see FileSeekableStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||
|
||||
private byte[] buffer;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||
* {@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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||
* {@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 pTempDir optional temp directory
|
||||
*
|
||||
* @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
|
||||
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
|
||||
}
|
||||
|
||||
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
|
||||
Validate.notNull(pTempBaseName, "tempBaseName");
|
||||
|
||||
File file = File.createTempFile(pTempBaseName, null, pTempDir);
|
||||
file.deleteOnExit();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
// TODO: Consider exposing this for external use
|
||||
/*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];
|
||||
}
|
||||
|
||||
public final boolean isCachedMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isCachedFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeImpl() throws IOException {
|
||||
// TODO: Close cache file
|
||||
super.closeImpl();
|
||||
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int read;
|
||||
if (position == streamPosition) {
|
||||
// Read ahead into buffer, for performance
|
||||
read = readAhead(buffer, 0, buffer.length);
|
||||
if (read >= 0) {
|
||||
read = buffer[0] & 0xff;
|
||||
}
|
||||
|
||||
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
|
||||
}
|
||||
else {
|
||||
// ..or read byte from the cache
|
||||
syncPosition();
|
||||
read = getCache().read();
|
||||
|
||||
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
|
||||
}
|
||||
|
||||
// TODO: This field is not REALLY considered accessible.. :-P
|
||||
if (read != -1) {
|
||||
position++;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int length;
|
||||
if (position == streamPosition) {
|
||||
// Read bytes from the stream
|
||||
length = readAhead(pBytes, pOffset, pLength);
|
||||
|
||||
//System.out.println("Read " + length + " byte from stream");
|
||||
}
|
||||
else {
|
||||
// ...or read bytes from the cache
|
||||
syncPosition();
|
||||
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
|
||||
|
||||
//System.out.println("Read " + length + " byte from cache");
|
||||
}
|
||||
|
||||
// TODO: This field is not REALLY considered accessible.. :-P
|
||||
if (length > 0) {
|
||||
position += length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
int length;
|
||||
length = stream.read(pBytes, pOffset, pLength);
|
||||
|
||||
if (length > 0) {
|
||||
streamPosition += length;
|
||||
getCache().write(pBytes, pOffset, length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
|
||||
|
||||
final static class FileCache extends StreamCache {
|
||||
private RandomAccessFile cacheFile;
|
||||
|
||||
public FileCache(final File pFile) throws FileNotFoundException {
|
||||
Validate.notNull(pFile, "file");
|
||||
cacheFile = new RandomAccessFile(pFile, "rw");
|
||||
}
|
||||
|
||||
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 {
|
||||
cacheFile.write(pBuffer, pOffset, pLength);
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return cacheFile.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
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 long getPosition() throws IOException {
|
||||
return cacheFile.getFilePointer();
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
cacheFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
|
||||
* <p>
|
||||
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
|
||||
* </p>
|
||||
*
|
||||
* @see MemoryCacheSeekableStream
|
||||
* @see FileSeekableStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||
|
||||
private byte[] buffer;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||
* {@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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||
* {@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 pTempDir optional temp directory
|
||||
*
|
||||
* @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
|
||||
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
|
||||
}
|
||||
|
||||
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
|
||||
Validate.notNull(pTempBaseName, "tempBaseName");
|
||||
|
||||
File file = File.createTempFile(pTempBaseName, null, pTempDir);
|
||||
file.deleteOnExit();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
// TODO: Consider exposing this for external use
|
||||
/*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];
|
||||
}
|
||||
|
||||
public final boolean isCachedMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isCachedFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeImpl() throws IOException {
|
||||
// TODO: Close cache file
|
||||
super.closeImpl();
|
||||
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int read;
|
||||
if (position == streamPosition) {
|
||||
// Read ahead into buffer, for performance
|
||||
read = readAhead(buffer, 0, buffer.length);
|
||||
if (read >= 0) {
|
||||
read = buffer[0] & 0xff;
|
||||
}
|
||||
|
||||
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
|
||||
}
|
||||
else {
|
||||
// ..or read byte from the cache
|
||||
syncPosition();
|
||||
read = getCache().read();
|
||||
|
||||
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
|
||||
}
|
||||
|
||||
// TODO: This field is not REALLY considered accessible.. :-P
|
||||
if (read != -1) {
|
||||
position++;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int length;
|
||||
if (position == streamPosition) {
|
||||
// Read bytes from the stream
|
||||
length = readAhead(pBytes, pOffset, pLength);
|
||||
|
||||
//System.out.println("Read " + length + " byte from stream");
|
||||
}
|
||||
else {
|
||||
// ...or read bytes from the cache
|
||||
syncPosition();
|
||||
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
|
||||
|
||||
//System.out.println("Read " + length + " byte from cache");
|
||||
}
|
||||
|
||||
// TODO: This field is not REALLY considered accessible.. :-P
|
||||
if (length > 0) {
|
||||
position += length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
int length;
|
||||
length = stream.read(pBytes, pOffset, pLength);
|
||||
|
||||
if (length > 0) {
|
||||
streamPosition += length;
|
||||
getCache().write(pBytes, pOffset, length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
|
||||
|
||||
final static class FileCache extends StreamCache {
|
||||
private RandomAccessFile cacheFile;
|
||||
|
||||
public FileCache(final File pFile) throws FileNotFoundException {
|
||||
Validate.notNull(pFile, "file");
|
||||
cacheFile = new RandomAccessFile(pFile, "rw");
|
||||
}
|
||||
|
||||
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 {
|
||||
cacheFile.write(pBuffer, pOffset, pLength);
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return cacheFile.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
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 long getPosition() throws IOException {
|
||||
return cacheFile.getFilePointer();
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
cacheFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
|
||||
* <p/>
|
||||
* @see FileCacheSeekableStream
|
||||
* @see MemoryCacheSeekableStream
|
||||
* @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/FileSeekableStream.java#4 $
|
||||
*/
|
||||
public final class FileSeekableStream extends SeekableInputStream {
|
||||
|
||||
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
|
||||
// my tests..?
|
||||
|
||||
final RandomAccessFile mRandomAccess;
|
||||
|
||||
/**
|
||||
* Creates a {@code FileSeekableStream} that reads from the given
|
||||
* {@code File}.
|
||||
*
|
||||
* @param pInput file to read from
|
||||
* @throws FileNotFoundException if {@code pInput} does not exist
|
||||
*/
|
||||
public FileSeekableStream(final File pInput) throws FileNotFoundException {
|
||||
this(new RandomAccessFile(pInput, "r"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileSeekableStream} that reads from the given file.
|
||||
* The {@code RandomAccessFile} needs only to be open in read
|
||||
* ({@code "r"}) mode.
|
||||
*
|
||||
* @param pInput file to read from
|
||||
*/
|
||||
public FileSeekableStream(final RandomAccessFile pInput) {
|
||||
mRandomAccess = pInput;
|
||||
}
|
||||
|
||||
/// Seekable
|
||||
|
||||
public boolean isCached() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCachedFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCachedMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// InputStream
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
long length = mRandomAccess.length() - position;
|
||||
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
|
||||
}
|
||||
|
||||
public void closeImpl() throws IOException {
|
||||
mRandomAccess.close();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int read = mRandomAccess.read();
|
||||
if (read >= 0) {
|
||||
position++;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int read = mRandomAccess.read(pBytes, pOffset, pLength);
|
||||
if (read > 0) {
|
||||
position += read;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing, as we don't really do any caching here.
|
||||
*
|
||||
* @param pPosition the position to flush to
|
||||
*/
|
||||
protected void flushBeforeImpl(long pPosition) {
|
||||
}
|
||||
|
||||
protected void seekImpl(long pPosition) throws IOException {
|
||||
mRandomAccess.seek(pPosition);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
|
||||
|
||||
* @see FileCacheSeekableStream
|
||||
* @see MemoryCacheSeekableStream
|
||||
* @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/FileSeekableStream.java#4 $
|
||||
*/
|
||||
public final class FileSeekableStream extends SeekableInputStream {
|
||||
|
||||
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
|
||||
// my tests..?
|
||||
|
||||
final RandomAccessFile mRandomAccess;
|
||||
|
||||
/**
|
||||
* Creates a {@code FileSeekableStream} that reads from the given
|
||||
* {@code File}.
|
||||
*
|
||||
* @param pInput file to read from
|
||||
* @throws FileNotFoundException if {@code pInput} does not exist
|
||||
*/
|
||||
public FileSeekableStream(final File pInput) throws FileNotFoundException {
|
||||
this(new RandomAccessFile(pInput, "r"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileSeekableStream} that reads from the given file.
|
||||
* The {@code RandomAccessFile} needs only to be open in read
|
||||
* ({@code "r"}) mode.
|
||||
*
|
||||
* @param pInput file to read from
|
||||
*/
|
||||
public FileSeekableStream(final RandomAccessFile pInput) {
|
||||
mRandomAccess = pInput;
|
||||
}
|
||||
|
||||
/// Seekable
|
||||
|
||||
public boolean isCached() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCachedFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCachedMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// InputStream
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
long length = mRandomAccess.length() - position;
|
||||
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
|
||||
}
|
||||
|
||||
public void closeImpl() throws IOException {
|
||||
mRandomAccess.close();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int read = mRandomAccess.read();
|
||||
if (read >= 0) {
|
||||
position++;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
int read = mRandomAccess.read(pBytes, pOffset, pLength);
|
||||
if (read > 0) {
|
||||
position += read;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing, as we don't really do any caching here.
|
||||
*
|
||||
* @param pPosition the position to flush to
|
||||
*/
|
||||
protected void flushBeforeImpl(long pPosition) {
|
||||
}
|
||||
|
||||
protected void seekImpl(long pPosition) throws IOException {
|
||||
mRandomAccess.seek(pPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,103 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* FileSystem
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: FileSystem.java#1 $
|
||||
*/
|
||||
abstract class FileSystem {
|
||||
abstract long getFreeSpace(File pPath);
|
||||
|
||||
abstract long getTotalSpace(File pPath);
|
||||
|
||||
abstract String getName();
|
||||
|
||||
static BufferedReader exec(String[] pArgs) throws IOException {
|
||||
Process cmd = Runtime.getRuntime().exec(pArgs);
|
||||
return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
|
||||
}
|
||||
|
||||
static FileSystem get() {
|
||||
String os = System.getProperty("os.name");
|
||||
//System.out.println("os = " + os);
|
||||
|
||||
os = os.toLowerCase();
|
||||
if (os.contains("windows")) {
|
||||
return new Win32FileSystem();
|
||||
}
|
||||
else if (os.contains("linux") ||
|
||||
os.contains("sun os") ||
|
||||
os.contains("sunos") ||
|
||||
os.contains("solaris") ||
|
||||
os.contains("mpe/ix") ||
|
||||
os.contains("hp-ux") ||
|
||||
os.contains("aix") ||
|
||||
os.contains("freebsd") ||
|
||||
os.contains("irix") ||
|
||||
os.contains("digital unix") ||
|
||||
os.contains("unix") ||
|
||||
os.contains("mac os x")) {
|
||||
return new UnixFileSystem();
|
||||
}
|
||||
else {
|
||||
return new UnknownFileSystem(os);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnknownFileSystem extends FileSystem {
|
||||
private final String osName;
|
||||
|
||||
UnknownFileSystem(String pOSName) {
|
||||
osName = pOSName;
|
||||
}
|
||||
|
||||
long getFreeSpace(File pPath) {
|
||||
return 0l;
|
||||
}
|
||||
|
||||
long getTotalSpace(File pPath) {
|
||||
return 0l;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return "Unknown (" + osName + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* FileSystem
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: FileSystem.java#1 $
|
||||
*/
|
||||
abstract class FileSystem {
|
||||
abstract long getFreeSpace(File pPath);
|
||||
|
||||
abstract long getTotalSpace(File pPath);
|
||||
|
||||
abstract String getName();
|
||||
|
||||
static BufferedReader exec(String[] pArgs) throws IOException {
|
||||
Process cmd = Runtime.getRuntime().exec(pArgs);
|
||||
return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
|
||||
}
|
||||
|
||||
static FileSystem get() {
|
||||
String os = System.getProperty("os.name");
|
||||
//System.out.println("os = " + os);
|
||||
|
||||
os = os.toLowerCase();
|
||||
if (os.contains("windows")) {
|
||||
return new Win32FileSystem();
|
||||
}
|
||||
else if (os.contains("linux") ||
|
||||
os.contains("sun os") ||
|
||||
os.contains("sunos") ||
|
||||
os.contains("solaris") ||
|
||||
os.contains("mpe/ix") ||
|
||||
os.contains("hp-ux") ||
|
||||
os.contains("aix") ||
|
||||
os.contains("freebsd") ||
|
||||
os.contains("irix") ||
|
||||
os.contains("digital unix") ||
|
||||
os.contains("unix") ||
|
||||
os.contains("mac os x")) {
|
||||
return new UnixFileSystem();
|
||||
}
|
||||
else {
|
||||
return new UnknownFileSystem(os);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnknownFileSystem extends FileSystem {
|
||||
private final String osName;
|
||||
|
||||
UnknownFileSystem(String pOSName) {
|
||||
osName = pOSName;
|
||||
}
|
||||
|
||||
long getFreeSpace(File pPath) {
|
||||
return 0l;
|
||||
}
|
||||
|
||||
long getTotalSpace(File pPath) {
|
||||
return 0l;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return "Unknown (" + osName + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,244 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.util.regex.WildcardStringParser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
|
||||
/**
|
||||
* A Java Bean used for approving file names which are to be included in a
|
||||
* {@code java.io.File} listing.
|
||||
* The mask is given as a well-known DOS filename format, with '*' and '?' as
|
||||
* wildcards.
|
||||
* All other characters counts as ordinary characters.
|
||||
* <p/>
|
||||
* The file name masks are used as a filter input and is given to the class via
|
||||
* the string array property:<br>
|
||||
* <dd>{@code filenameMasksForInclusion} - Filename mask for exclusion of
|
||||
* files (default if both properties are defined)
|
||||
* <dd>{@code filenameMasksForExclusion} - Filename mask for exclusion of
|
||||
* files.
|
||||
* <p/>
|
||||
* A recommended way of doing this is by referencing to the component which uses
|
||||
* this class for file listing. In this way all properties are set in the same
|
||||
* component and this utility component is kept in the background with only
|
||||
* initial configuration necessary.
|
||||
*
|
||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
||||
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
||||
* @see FilenameFilter java.io.FilenameFilter
|
||||
* @see WildcardStringParser
|
||||
* @deprecated
|
||||
*/
|
||||
public class FilenameMaskFilter implements FilenameFilter {
|
||||
|
||||
// TODO: Rewrite to use regexp, or create new class
|
||||
|
||||
// Members
|
||||
private String[] filenameMasksForInclusion;
|
||||
private String[] filenameMasksForExclusion;
|
||||
private boolean inclusion = true;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*/
|
||||
public FilenameMaskFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMask the filename mask
|
||||
*/
|
||||
public FilenameMaskFilter(final String pFilenameMask) {
|
||||
String[] filenameMask = {pFilenameMask};
|
||||
setFilenameMasksForInclusion(filenameMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMasks the filename masks
|
||||
*/
|
||||
public FilenameMaskFilter(final String[] pFilenameMasks) {
|
||||
this(pFilenameMasks, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMask the filename masks
|
||||
* @param pExclusion if {@code true}, the masks will be excluded
|
||||
*/
|
||||
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
|
||||
String[] filenameMask = {pFilenameMask};
|
||||
|
||||
if (pExclusion) {
|
||||
setFilenameMasksForExclusion(filenameMask);
|
||||
}
|
||||
else {
|
||||
setFilenameMasksForInclusion(filenameMask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMasks the filename masks
|
||||
* @param pExclusion if {@code true}, the masks will be excluded
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current inclusion masks
|
||||
*/
|
||||
public String[] getFilenameMasksForInclusion() {
|
||||
return filenameMasksForInclusion.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pFilenameMasksForExclusion the filename masks to exclude
|
||||
*/
|
||||
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
|
||||
filenameMasksForExclusion = pFilenameMasksForExclusion;
|
||||
inclusion = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current exclusion masks
|
||||
*/
|
||||
public String[] getFilenameMasksForExclusion() {
|
||||
return filenameMasksForExclusion.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method implements the {@code java.io.FilenameFilter} interface.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public boolean accept(File pDir, String pName) {
|
||||
WildcardStringParser parser;
|
||||
|
||||
// Check each filename string mask whether the file is to be accepted
|
||||
if (inclusion) { // Inclusion
|
||||
for (String mask : filenameMasksForInclusion) {
|
||||
parser = new WildcardStringParser(mask);
|
||||
if (parser.parseString(pName)) {
|
||||
|
||||
// The filename was accepted by the filename masks provided
|
||||
// - include it in filename list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The filename not was accepted by any of the filename masks
|
||||
// provided - NOT to be included in the filename list
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Exclusion
|
||||
for (String mask : filenameMasksForExclusion) {
|
||||
parser = new WildcardStringParser(mask);
|
||||
if (parser.parseString(pName)) {
|
||||
|
||||
// The filename was accepted by the filename masks provided
|
||||
// - NOT to be included in the filename list
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The filename was not accepted by any of the filename masks
|
||||
// provided - include it in filename list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string representation for debug purposes
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
int i;
|
||||
|
||||
if (inclusion) {
|
||||
// Inclusion
|
||||
if (filenameMasksForInclusion == null) {
|
||||
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
|
||||
}
|
||||
else {
|
||||
retVal.append(filenameMasksForInclusion.length);
|
||||
retVal.append(" filename mask(s) - ");
|
||||
for (i = 0; i < filenameMasksForInclusion.length; i++) {
|
||||
retVal.append("\"");
|
||||
retVal.append(filenameMasksForInclusion[i]);
|
||||
retVal.append("\", \"");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Exclusion
|
||||
if (filenameMasksForExclusion == null) {
|
||||
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
|
||||
}
|
||||
else {
|
||||
retVal.append(filenameMasksForExclusion.length);
|
||||
retVal.append(" exclusion filename mask(s) - ");
|
||||
for (i = 0; i < filenameMasksForExclusion.length; i++) {
|
||||
retVal.append("\"");
|
||||
retVal.append(filenameMasksForExclusion[i]);
|
||||
retVal.append("\", \"");
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal.toString();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.util.regex.WildcardStringParser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
|
||||
/**
|
||||
* A Java Bean used for approving file names which are to be included in a
|
||||
* {@code java.io.File} listing.
|
||||
* The mask is given as a well-known DOS filename format, with '*' and '?' as
|
||||
* wildcards.
|
||||
* All other characters counts as ordinary characters.
|
||||
* <p>
|
||||
* The file name masks are used as a filter input and is given to the class via
|
||||
* the string array property:
|
||||
* </p>
|
||||
* <dl>
|
||||
* <dt>{@code filenameMasksForInclusion}</dt>
|
||||
* <dd>Filename mask for exclusion of
|
||||
* files (default if both properties are defined).</dd>
|
||||
* <dt>{@code filenameMasksForExclusion}</dt>
|
||||
* <dd>Filename mask for exclusion of files.</dd>
|
||||
* </dl>
|
||||
* <p>
|
||||
* A recommended way of doing this is by referencing to the component which uses
|
||||
* this class for file listing. In this way all properties are set in the same
|
||||
* component and this utility component is kept in the background with only
|
||||
* initial configuration necessary.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
||||
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
||||
* @see FilenameFilter java.io.FilenameFilter
|
||||
* @see WildcardStringParser
|
||||
* @deprecated
|
||||
*/
|
||||
public class FilenameMaskFilter implements FilenameFilter {
|
||||
|
||||
// TODO: Rewrite to use regexp, or create new class
|
||||
|
||||
// Members
|
||||
private String[] filenameMasksForInclusion;
|
||||
private String[] filenameMasksForExclusion;
|
||||
private boolean inclusion = true;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*/
|
||||
public FilenameMaskFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMask the filename mask
|
||||
*/
|
||||
public FilenameMaskFilter(final String pFilenameMask) {
|
||||
String[] filenameMask = {pFilenameMask};
|
||||
setFilenameMasksForInclusion(filenameMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMasks the filename masks
|
||||
*/
|
||||
public FilenameMaskFilter(final String[] pFilenameMasks) {
|
||||
this(pFilenameMasks, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMask the filename masks
|
||||
* @param pExclusion if {@code true}, the masks will be excluded
|
||||
*/
|
||||
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
|
||||
String[] filenameMask = {pFilenameMask};
|
||||
|
||||
if (pExclusion) {
|
||||
setFilenameMasksForExclusion(filenameMask);
|
||||
}
|
||||
else {
|
||||
setFilenameMasksForInclusion(filenameMask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FilenameMaskFilter}
|
||||
*
|
||||
* @param pFilenameMasks the filename masks
|
||||
* @param pExclusion if {@code true}, the masks will be excluded
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current inclusion masks
|
||||
*/
|
||||
public String[] getFilenameMasksForInclusion() {
|
||||
return filenameMasksForInclusion.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pFilenameMasksForExclusion the filename masks to exclude
|
||||
*/
|
||||
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
|
||||
filenameMasksForExclusion = pFilenameMasksForExclusion;
|
||||
inclusion = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current exclusion masks
|
||||
*/
|
||||
public String[] getFilenameMasksForExclusion() {
|
||||
return filenameMasksForExclusion.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method implements the {@code java.io.FilenameFilter} interface.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public boolean accept(File pDir, String pName) {
|
||||
WildcardStringParser parser;
|
||||
|
||||
// Check each filename string mask whether the file is to be accepted
|
||||
if (inclusion) { // Inclusion
|
||||
for (String mask : filenameMasksForInclusion) {
|
||||
parser = new WildcardStringParser(mask);
|
||||
if (parser.parseString(pName)) {
|
||||
|
||||
// The filename was accepted by the filename masks provided
|
||||
// - include it in filename list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The filename not was accepted by any of the filename masks
|
||||
// provided - NOT to be included in the filename list
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Exclusion
|
||||
for (String mask : filenameMasksForExclusion) {
|
||||
parser = new WildcardStringParser(mask);
|
||||
if (parser.parseString(pName)) {
|
||||
|
||||
// The filename was accepted by the filename masks provided
|
||||
// - NOT to be included in the filename list
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The filename was not accepted by any of the filename masks
|
||||
// provided - include it in filename list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string representation for debug purposes
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
int i;
|
||||
|
||||
if (inclusion) {
|
||||
// Inclusion
|
||||
if (filenameMasksForInclusion == null) {
|
||||
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
|
||||
}
|
||||
else {
|
||||
retVal.append(filenameMasksForInclusion.length);
|
||||
retVal.append(" filename mask(s) - ");
|
||||
for (i = 0; i < filenameMasksForInclusion.length; i++) {
|
||||
retVal.append("\"");
|
||||
retVal.append(filenameMasksForInclusion[i]);
|
||||
retVal.append("\", \"");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Exclusion
|
||||
if (filenameMasksForExclusion == null) {
|
||||
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
|
||||
}
|
||||
else {
|
||||
retVal.append(filenameMasksForExclusion.length);
|
||||
retVal.append(" exclusion filename mask(s) - ");
|
||||
for (i = 0; i < filenameMasksForExclusion.length; i++) {
|
||||
retVal.append("\"");
|
||||
retVal.append(filenameMasksForExclusion[i]);
|
||||
retVal.append("\", \"");
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,452 +1,449 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
* significant product on one of my examples, but that's not required either.
|
||||
*
|
||||
* Elliotte Rusty Harold
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A little endian input stream reads two's complement,
|
||||
* little endian integers, floating point numbers, and characters
|
||||
* and returns them as Java primitive types.
|
||||
* <p/>
|
||||
* The standard {@code java.io.DataInputStream} class
|
||||
* which this class imitates reads big endian quantities.
|
||||
* <p/>
|
||||
* <em>Warning:
|
||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||
* specifies big endian byte order in their documentation.
|
||||
* This means that this class is, strictly speaking, not a proper
|
||||
* implementation. However, I don't see a reason for the these interfaces to
|
||||
* specify the byte order of their underlying representations.
|
||||
* </em>
|
||||
*
|
||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||
* @see java.io.DataInputStream
|
||||
* @see java.io.DataInput
|
||||
* @see java.io.DataOutput
|
||||
*
|
||||
* @author Elliotte Rusty Harold
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version 2
|
||||
*/
|
||||
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
|
||||
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
|
||||
/**
|
||||
* Creates a new little endian input stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return the {@code boolean} value read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public boolean readBoolean() throws IOException {
|
||||
int b = in.read();
|
||||
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed {@code byte} from the underlying input stream
|
||||
* with value between -128 and 127
|
||||
*
|
||||
* @return the {@code byte} value read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public byte readByte() throws IOException {
|
||||
int b = in.read();
|
||||
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte) b;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned {@code byte} from the underlying
|
||||
* input stream with value between 0 and 255
|
||||
*
|
||||
* @return the {@code byte} value read.
|
||||
* @throws EOFException if the end of the underlying input
|
||||
* stream has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public int readUnsignedByte() throws IOException {
|
||||
int b = in.read();
|
||||
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a two byte signed {@code short} from the underlying
|
||||
* input stream in little endian order, low byte first.
|
||||
*
|
||||
* @return the {@code short} read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public short readShort() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
// only need to test last byte read
|
||||
// if byte1 is -1 so is byte2
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return the int value of the unsigned short read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public int readUnsignedShort() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte2 << 8) + byte1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a two byte Unicode {@code char} from the underlying
|
||||
* input stream in little endian order, low byte first.
|
||||
*
|
||||
* @return the int value of the unsigned short read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public char readChar() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return the {@code int} read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public int readInt() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
int byte3 = in.read();
|
||||
int byte4 = in.read();
|
||||
|
||||
if (byte4 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return the {@code int} read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public long readLong() throws IOException {
|
||||
long byte1 = in.read();
|
||||
long byte2 = in.read();
|
||||
long byte3 = in.read();
|
||||
long byte4 = in.read();
|
||||
long byte5 = in.read();
|
||||
long byte6 = in.read();
|
||||
long byte7 = in.read();
|
||||
long byte8 = in.read();
|
||||
|
||||
if (byte8 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte8 << 56) | ((byte7 << 56) >>> 8)
|
||||
| ((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
|
||||
* in <b>big</b> endian order as required by the
|
||||
* UTF-8 specification. This gives the number of bytes in
|
||||
* the UTF-8 encoded version of the string.
|
||||
* Next this many bytes are read and decoded as UTF-8
|
||||
* encoded characters.
|
||||
*
|
||||
* @return the decoded string
|
||||
* @throws UTFDataFormatException if the string cannot be decoded
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public String readUTF() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
int numbytes = (byte1 << 8) + byte2;
|
||||
char result[] = new char[numbytes];
|
||||
int numread = 0;
|
||||
int numchars = 0;
|
||||
|
||||
while (numread < numbytes) {
|
||||
int c1 = readUnsignedByte();
|
||||
int c2, c3;
|
||||
|
||||
// The first four bits of c1 determine how many bytes are in this char
|
||||
int test = c1 >> 4;
|
||||
if (test < 8) { // one byte
|
||||
numread++;
|
||||
result[numchars++] = (char) c1;
|
||||
}
|
||||
else if (test == 12 || test == 13) { // two bytes
|
||||
numread += 2;
|
||||
|
||||
if (numread > numbytes) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
c2 = readUnsignedByte();
|
||||
|
||||
if ((c2 & 0xC0) != 0x80) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
||||
}
|
||||
else if (test == 14) { // three bytes
|
||||
numread += 3;
|
||||
|
||||
if (numread > numbytes) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
c2 = readUnsignedByte();
|
||||
c3 = readUnsignedByte();
|
||||
|
||||
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
||||
}
|
||||
else { // malformed
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
} // 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
|
||||
* have been read.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
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
|
||||
* have been read.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public final float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code skipBytes}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained input stream.
|
||||
*
|
||||
* @param pLength the number of bytes to be skipped.
|
||||
* @return the actual number of bytes skipped.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int skipBytes(int pLength) throws IOException {
|
||||
// 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...
|
||||
int total = 0;
|
||||
int skipped;
|
||||
|
||||
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
|
||||
total += skipped;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFully}
|
||||
* method of {@code DataInput}.
|
||||
* <p/>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @param pBytes the buffer into which the data is read.
|
||||
* @throws EOFException if this input stream reaches the end before
|
||||
* reading all the bytes.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public final void readFully(byte pBytes[]) throws IOException {
|
||||
readFully(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFully}
|
||||
* method of {@code DataInput}.
|
||||
* <p/>
|
||||
* Bytes
|
||||
* 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 pLength the number of bytes to read.
|
||||
* @throws EOFException if this input stream reaches the end before
|
||||
* reading all the bytes.
|
||||
* @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) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (count < pLength) {
|
||||
int read = in.read(pBytes, pOffset + count, pLength - count);
|
||||
|
||||
if (read < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
count += read;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readLine}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained input stream.
|
||||
*
|
||||
* @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.
|
||||
* @see java.io.BufferedReader#readLine()
|
||||
* @see java.io.DataInputStream#readLine()
|
||||
* @noinspection deprecation
|
||||
*/
|
||||
public String readLine() throws IOException {
|
||||
DataInputStream ds = new DataInputStream(in);
|
||||
return ds.readLine();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
* significant product on one of my examples, but that's not required either.
|
||||
*
|
||||
* Elliotte Rusty Harold
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A little endian input stream reads two's complement,
|
||||
* little endian integers, floating point numbers, and characters
|
||||
* and returns them as Java primitive types.
|
||||
* <p>
|
||||
* The standard {@code java.io.DataInputStream} class
|
||||
* which this class imitates reads big endian quantities.
|
||||
* </p>
|
||||
* <p>
|
||||
* <em>Warning:
|
||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||
* specifies big endian byte order in their documentation.
|
||||
* This means that this class is, strictly speaking, not a proper
|
||||
* implementation. However, I don't see a reason for the these interfaces to
|
||||
* specify the byte order of their underlying representations.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||
* @see java.io.DataInputStream
|
||||
* @see java.io.DataInput
|
||||
* @see java.io.DataOutput
|
||||
*
|
||||
* @author Elliotte Rusty Harold
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version 2
|
||||
*/
|
||||
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
|
||||
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
|
||||
/**
|
||||
* Creates a new little endian input stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return the {@code boolean} value read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public boolean readBoolean() throws IOException {
|
||||
int b = in.read();
|
||||
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed {@code byte} from the underlying input stream
|
||||
* with value between -128 and 127
|
||||
*
|
||||
* @return the {@code byte} value read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public byte readByte() throws IOException {
|
||||
int b = in.read();
|
||||
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte) b;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned {@code byte} from the underlying
|
||||
* input stream with value between 0 and 255
|
||||
*
|
||||
* @return the {@code byte} value read.
|
||||
* @throws EOFException if the end of the underlying input
|
||||
* stream has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public int readUnsignedByte() throws IOException {
|
||||
int b = in.read();
|
||||
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a two byte signed {@code short} from the underlying
|
||||
* input stream in little endian order, low byte first.
|
||||
*
|
||||
* @return the {@code short} read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public short readShort() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
// only need to test last byte read
|
||||
// if byte1 is -1 so is byte2
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return the int value of the unsigned short read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public int readUnsignedShort() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte2 << 8) + byte1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a two byte Unicode {@code char} from the underlying
|
||||
* input stream in little endian order, low byte first.
|
||||
*
|
||||
* @return the int value of the unsigned short read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public char readChar() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return the {@code int} read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public int readInt() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
int byte3 = in.read();
|
||||
int byte4 = in.read();
|
||||
|
||||
if (byte4 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return the {@code int} read.
|
||||
* @throws EOFException if the end of the underlying input stream
|
||||
* has been reached
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public long readLong() throws IOException {
|
||||
long byte1 = in.read();
|
||||
long byte2 = in.read();
|
||||
long byte3 = in.read();
|
||||
long byte4 = in.read();
|
||||
long byte5 = in.read();
|
||||
long byte6 = in.read();
|
||||
long byte7 = in.read();
|
||||
long byte8 = in.read();
|
||||
|
||||
if (byte8 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte8 << 56) | ((byte7 << 56) >>> 8)
|
||||
| ((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
|
||||
* in <b>big</b> endian order as required by the
|
||||
* UTF-8 specification. This gives the number of bytes in
|
||||
* the UTF-8 encoded version of the string.
|
||||
* Next this many bytes are read and decoded as UTF-8
|
||||
* encoded characters.
|
||||
*
|
||||
* @return the decoded string
|
||||
* @throws UTFDataFormatException if the string cannot be decoded
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public String readUTF() throws IOException {
|
||||
int byte1 = in.read();
|
||||
int byte2 = in.read();
|
||||
|
||||
if (byte2 < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
int numbytes = (byte1 << 8) + byte2;
|
||||
char result[] = new char[numbytes];
|
||||
int numread = 0;
|
||||
int numchars = 0;
|
||||
|
||||
while (numread < numbytes) {
|
||||
int c1 = readUnsignedByte();
|
||||
int c2, c3;
|
||||
|
||||
// The first four bits of c1 determine how many bytes are in this char
|
||||
int test = c1 >> 4;
|
||||
if (test < 8) { // one byte
|
||||
numread++;
|
||||
result[numchars++] = (char) c1;
|
||||
}
|
||||
else if (test == 12 || test == 13) { // two bytes
|
||||
numread += 2;
|
||||
|
||||
if (numread > numbytes) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
c2 = readUnsignedByte();
|
||||
|
||||
if ((c2 & 0xC0) != 0x80) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
||||
}
|
||||
else if (test == 14) { // three bytes
|
||||
numread += 3;
|
||||
|
||||
if (numread > numbytes) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
c2 = readUnsignedByte();
|
||||
c3 = readUnsignedByte();
|
||||
|
||||
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
||||
}
|
||||
else { // malformed
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
} // 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
|
||||
* have been read.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
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
|
||||
* have been read.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public final float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code skipBytes}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained input stream.
|
||||
*
|
||||
* @param pLength the number of bytes to be skipped.
|
||||
* @return the actual number of bytes skipped.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int skipBytes(int pLength) throws IOException {
|
||||
// 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...
|
||||
int total = 0;
|
||||
int skipped;
|
||||
|
||||
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
|
||||
total += skipped;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFully} method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained input stream.
|
||||
* </p>
|
||||
*
|
||||
* @param pBytes the buffer into which the data is read.
|
||||
* @throws EOFException if this input stream reaches the end before
|
||||
* reading all the bytes.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public final void readFully(byte pBytes[]) throws IOException {
|
||||
readFully(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFully} method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained input stream.
|
||||
* </p>
|
||||
*
|
||||
* @param pBytes the buffer into which the data is read.
|
||||
* @param pOffset the start offset of the data.
|
||||
* @param pLength the number of bytes to read.
|
||||
* @throws EOFException if this input stream reaches the end before
|
||||
* reading all the bytes.
|
||||
* @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) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (count < pLength) {
|
||||
int read = in.read(pBytes, pOffset + count, pLength - count);
|
||||
|
||||
if (read < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
count += read;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readLine}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained input stream.
|
||||
*
|
||||
* @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.
|
||||
* @see java.io.BufferedReader#readLine()
|
||||
* @see java.io.DataInputStream#readLine()
|
||||
*/
|
||||
public String readLine() throws IOException {
|
||||
DataInputStream ds = new DataInputStream(in);
|
||||
return ds.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,340 +1,342 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
* significant product on one of my examples, but that's not required either.
|
||||
*
|
||||
* Elliotte Rusty Harold
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A little endian output stream writes primitive Java numbers
|
||||
* and characters to an output stream in a little endian format.
|
||||
* <p/>
|
||||
* The standard {@code java.io.DataOutputStream} class which this class
|
||||
* imitates uses big endian integers.
|
||||
* <p/>
|
||||
* <em>Warning:
|
||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||
* specifies big endian byte order in their documentation.
|
||||
* This means that this class is, strictly speaking, not a proper
|
||||
* implementation. However, I don't see a reason for the these interfaces to
|
||||
* specify the byte order of their underlying representations.
|
||||
* </em>
|
||||
*
|
||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||
* @see java.io.DataOutputStream
|
||||
* @see java.io.DataInput
|
||||
* @see java.io.DataOutput
|
||||
*
|
||||
* @author Elliotte Rusty Harold
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Creates a new little endian output stream and chains it to the
|
||||
* output stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @param pStream the underlying output stream.
|
||||
* @see java.io.FilterOutputStream#out
|
||||
*/
|
||||
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.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pBytes the data.
|
||||
* @param pOffset the start offset in the data.
|
||||
* @param pLength the number of bytes to write.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pBoolean the {@code boolean} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeBoolean(boolean pBoolean) throws IOException {
|
||||
if (pBoolean) {
|
||||
write(1);
|
||||
}
|
||||
else {
|
||||
write(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a {@code byte} to the underlying output stream
|
||||
*
|
||||
* @param pByte the {@code byte} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pShort the {@code short} to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeShort(int pShort) throws IOException {
|
||||
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.
|
||||
*
|
||||
* @param pChar the {@code char} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeChar(int pChar) throws IOException {
|
||||
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
|
||||
*
|
||||
* @param pInt the {@code int} to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeInt(int pInt) throws IOException {
|
||||
out.write(pInt & 0xFF);
|
||||
out.write((pInt >>> 8) & 0xFF);
|
||||
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
|
||||
*
|
||||
* @param pLong the {@code long} to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeLong(long pLong) throws IOException {
|
||||
out.write((int) pLong & 0xFF);
|
||||
out.write((int) (pLong >>> 8) & 0xFF);
|
||||
out.write((int) (pLong >>> 16) & 0xFF);
|
||||
out.write((int) (pLong >>> 24) & 0xFF);
|
||||
out.write((int) (pLong >>> 32) & 0xFF);
|
||||
out.write((int) (pLong >>> 40) & 0xFF);
|
||||
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.
|
||||
*
|
||||
* @param f the {@code float} value to be written.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param d the {@code double} value to be written.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pString the {@code String} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
* @see #writeByte(int)
|
||||
* @see #out
|
||||
*/
|
||||
public void writeBytes(String pString) throws IOException {
|
||||
int length = pString.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
out.write((byte) pString.charAt(i));
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param pString a {@code String} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
* @see #writeChar(int)
|
||||
* @see #out
|
||||
*/
|
||||
public void writeChars(String pString) throws IOException {
|
||||
int length = pString.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int c = pString.charAt(i);
|
||||
out.write(c & 0xFF);
|
||||
out.write((c >>> 8) & 0xFF);
|
||||
}
|
||||
|
||||
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
|
||||
* in <b>big</b> endian order as required by the
|
||||
* UTF-8 specification. This gives the number of bytes in the
|
||||
* UTF-8 encoded version of the string, not the number of characters
|
||||
* in the string. Next each character of the string is written
|
||||
* using the UTF-8 encoding for the character.
|
||||
*
|
||||
* @param pString the string to be written.
|
||||
* @throws UTFDataFormatException if the string is longer than
|
||||
* 65,535 characters.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeUTF(String pString) throws IOException {
|
||||
int numchars = pString.length();
|
||||
int numbytes = 0;
|
||||
|
||||
for (int i = 0; i < numchars; i++) {
|
||||
int c = pString.charAt(i);
|
||||
|
||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||
numbytes++;
|
||||
}
|
||||
else if (c > 0x07FF) {
|
||||
numbytes += 3;
|
||||
}
|
||||
else {
|
||||
numbytes += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (numbytes > 65535) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
out.write((numbytes >>> 8) & 0xFF);
|
||||
out.write(numbytes & 0xFF);
|
||||
|
||||
for (int i = 0; i < numchars; i++) {
|
||||
int c = pString.charAt(i);
|
||||
|
||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||
out.write(c);
|
||||
}
|
||||
else if (c > 0x07FF) {
|
||||
out.write(0xE0 | ((c >> 12) & 0x0F));
|
||||
out.write(0x80 | ((c >> 6) & 0x3F));
|
||||
out.write(0x80 | (c & 0x3F));
|
||||
bytesWritten += 2;
|
||||
}
|
||||
else {
|
||||
out.write(0xC0 | ((c >> 6) & 0x1F));
|
||||
out.write(0x80 | (c & 0x3F));
|
||||
bytesWritten += 1;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* number of bytes written.)
|
||||
* @return the value of the {@code written} field.
|
||||
* @see #bytesWritten
|
||||
*/
|
||||
public int size() {
|
||||
return bytesWritten;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
* significant product on one of my examples, but that's not required either.
|
||||
*
|
||||
* Elliotte Rusty Harold
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A little endian output stream writes primitive Java numbers
|
||||
* and characters to an output stream in a little endian format.
|
||||
* <p>
|
||||
* The standard {@code java.io.DataOutputStream} class which this class
|
||||
* imitates uses big endian integers.
|
||||
* </p>
|
||||
* <p>
|
||||
* <em>Warning:
|
||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||
* specifies big endian byte order in their documentation.
|
||||
* This means that this class is, strictly speaking, not a proper
|
||||
* implementation. However, I don't see a reason for the these interfaces to
|
||||
* specify the byte order of their underlying representations.
|
||||
* </em>
|
||||
* <p>
|
||||
*
|
||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||
* @see java.io.DataOutputStream
|
||||
* @see java.io.DataInput
|
||||
* @see java.io.DataOutput
|
||||
*
|
||||
* @author Elliotte Rusty Harold
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Creates a new little endian output stream and chains it to the
|
||||
* output stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @param pStream the underlying output stream.
|
||||
* @see java.io.FilterOutputStream#out
|
||||
*/
|
||||
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.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pBytes the data.
|
||||
* @param pOffset the start offset in the data.
|
||||
* @param pLength the number of bytes to write.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pBoolean the {@code boolean} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeBoolean(boolean pBoolean) throws IOException {
|
||||
if (pBoolean) {
|
||||
write(1);
|
||||
}
|
||||
else {
|
||||
write(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a {@code byte} to the underlying output stream
|
||||
*
|
||||
* @param pByte the {@code byte} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pShort the {@code short} to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeShort(int pShort) throws IOException {
|
||||
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.
|
||||
*
|
||||
* @param pChar the {@code char} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeChar(int pChar) throws IOException {
|
||||
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
|
||||
*
|
||||
* @param pInt the {@code int} to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeInt(int pInt) throws IOException {
|
||||
out.write(pInt & 0xFF);
|
||||
out.write((pInt >>> 8) & 0xFF);
|
||||
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
|
||||
*
|
||||
* @param pLong the {@code long} to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeLong(long pLong) throws IOException {
|
||||
out.write((int) pLong & 0xFF);
|
||||
out.write((int) (pLong >>> 8) & 0xFF);
|
||||
out.write((int) (pLong >>> 16) & 0xFF);
|
||||
out.write((int) (pLong >>> 24) & 0xFF);
|
||||
out.write((int) (pLong >>> 32) & 0xFF);
|
||||
out.write((int) (pLong >>> 40) & 0xFF);
|
||||
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.
|
||||
*
|
||||
* @param f the {@code float} value to be written.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param d the {@code double} value to be written.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pString the {@code String} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
* @see #writeByte(int)
|
||||
* @see #out
|
||||
*/
|
||||
public void writeBytes(String pString) throws IOException {
|
||||
int length = pString.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
out.write((byte) pString.charAt(i));
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param pString a {@code String} value to be written.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
* @see #writeChar(int)
|
||||
* @see #out
|
||||
*/
|
||||
public void writeChars(String pString) throws IOException {
|
||||
int length = pString.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int c = pString.charAt(i);
|
||||
out.write(c & 0xFF);
|
||||
out.write((c >>> 8) & 0xFF);
|
||||
}
|
||||
|
||||
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
|
||||
* in <b>big</b> endian order as required by the
|
||||
* UTF-8 specification. This gives the number of bytes in the
|
||||
* UTF-8 encoded version of the string, not the number of characters
|
||||
* in the string. Next each character of the string is written
|
||||
* using the UTF-8 encoding for the character.
|
||||
*
|
||||
* @param pString the string to be written.
|
||||
* @throws UTFDataFormatException if the string is longer than
|
||||
* 65,535 characters.
|
||||
* @throws IOException if the underlying stream throws an IOException.
|
||||
*/
|
||||
public void writeUTF(String pString) throws IOException {
|
||||
int numchars = pString.length();
|
||||
int numbytes = 0;
|
||||
|
||||
for (int i = 0; i < numchars; i++) {
|
||||
int c = pString.charAt(i);
|
||||
|
||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||
numbytes++;
|
||||
}
|
||||
else if (c > 0x07FF) {
|
||||
numbytes += 3;
|
||||
}
|
||||
else {
|
||||
numbytes += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (numbytes > 65535) {
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
out.write((numbytes >>> 8) & 0xFF);
|
||||
out.write(numbytes & 0xFF);
|
||||
|
||||
for (int i = 0; i < numchars; i++) {
|
||||
int c = pString.charAt(i);
|
||||
|
||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||
out.write(c);
|
||||
}
|
||||
else if (c > 0x07FF) {
|
||||
out.write(0xE0 | ((c >> 12) & 0x0F));
|
||||
out.write(0x80 | ((c >> 6) & 0x3F));
|
||||
out.write(0x80 | (c & 0x3F));
|
||||
bytesWritten += 2;
|
||||
}
|
||||
else {
|
||||
out.write(0xC0 | ((c >> 6) & 0x1F));
|
||||
out.write(0x80 | (c & 0x3F));
|
||||
bytesWritten += 1;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* number of bytes written.)
|
||||
* @return the value of the {@code written} field.
|
||||
* @see #bytesWritten
|
||||
*/
|
||||
public int size() {
|
||||
return bytesWritten;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,198 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@code SeekableInputStream} implementation that caches data in memory.
|
||||
* <p/>
|
||||
*
|
||||
* @see FileCacheSeekableStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||
|
||||
/**
|
||||
* Creates a {@code MemoryCacheSeekableStream}, reading from the given
|
||||
* {@code InputStream}. Data will be cached in memory.
|
||||
*
|
||||
* @param pStream the {@code InputStream} to read from.
|
||||
*/
|
||||
public MemoryCacheSeekableStream(final InputStream pStream) {
|
||||
super(pStream, new MemoryCache());
|
||||
}
|
||||
|
||||
public final boolean isCachedMemory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean isCachedFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
final static class MemoryCache extends StreamCache {
|
||||
final static int BLOCK_SIZE = 1 << 13;
|
||||
|
||||
private final List<byte[]> cache = new ArrayList<>();
|
||||
private long length;
|
||||
private long position;
|
||||
private long start;
|
||||
|
||||
private byte[] getBlock() throws IOException {
|
||||
final long currPos = position - start;
|
||||
if (currPos < 0) {
|
||||
throw new IOException("StreamCache flushed before read position");
|
||||
}
|
||||
|
||||
long index = currPos / BLOCK_SIZE;
|
||||
|
||||
if (index >= Integer.MAX_VALUE) {
|
||||
throw new IOException("Memory cache max size exceeded");
|
||||
}
|
||||
|
||||
if (index >= cache.size()) {
|
||||
try {
|
||||
cache.add(new byte[BLOCK_SIZE]);
|
||||
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
|
||||
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
|
||||
}
|
||||
catch (OutOfMemoryError e) {
|
||||
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
//System.out.println("index: " + index);
|
||||
|
||||
return cache.get((int) index);
|
||||
}
|
||||
|
||||
public void write(final int pByte) throws IOException {
|
||||
byte[] buffer = getBlock();
|
||||
|
||||
int idx = (int) (position % BLOCK_SIZE);
|
||||
buffer[idx] = (byte) pByte;
|
||||
position++;
|
||||
|
||||
if (position > length) {
|
||||
length = position;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: OptimizeMe!!!
|
||||
@Override
|
||||
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||
byte[] buffer = getBlock();
|
||||
for (int i = 0; i < pLength; i++) {
|
||||
int index = (int) position % BLOCK_SIZE;
|
||||
if (index == 0) {
|
||||
buffer = getBlock();
|
||||
}
|
||||
buffer[index] = pBuffer[pOffset + i];
|
||||
|
||||
position++;
|
||||
}
|
||||
if (position > length) {
|
||||
length = position;
|
||||
}
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (position >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte[] buffer = getBlock();
|
||||
|
||||
int idx = (int) (position % BLOCK_SIZE);
|
||||
position++;
|
||||
|
||||
return buffer[idx] & 0xff;
|
||||
}
|
||||
|
||||
// TODO: OptimizeMe!!!
|
||||
@Override
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (position >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte[] buffer = getBlock();
|
||||
|
||||
int bufferPos = (int) (position % BLOCK_SIZE);
|
||||
|
||||
// Find maxIdx and simplify test in for-loop
|
||||
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
|
||||
|
||||
int i;
|
||||
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
|
||||
for (i = 0; i < maxLen; i++) {
|
||||
pBytes[pOffset + i] = buffer[bufferPos + i];
|
||||
}
|
||||
|
||||
position += i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public void seek(final long pPosition) throws IOException {
|
||||
if (pPosition < start) {
|
||||
throw new IOException("Seek before flush position");
|
||||
}
|
||||
position = pPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(final long pPosition) {
|
||||
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
|
||||
|
||||
for (int i = 0; i < firstPos; i++) {
|
||||
cache.remove(0);
|
||||
}
|
||||
|
||||
start = pPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
public long getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@code SeekableInputStream} implementation that caches data in memory.
|
||||
*
|
||||
* @see FileCacheSeekableStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||
|
||||
/**
|
||||
* Creates a {@code MemoryCacheSeekableStream}, reading from the given
|
||||
* {@code InputStream}. Data will be cached in memory.
|
||||
*
|
||||
* @param pStream the {@code InputStream} to read from.
|
||||
*/
|
||||
public MemoryCacheSeekableStream(final InputStream pStream) {
|
||||
super(pStream, new MemoryCache());
|
||||
}
|
||||
|
||||
public final boolean isCachedMemory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean isCachedFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
final static class MemoryCache extends StreamCache {
|
||||
final static int BLOCK_SIZE = 1 << 13;
|
||||
|
||||
private final List<byte[]> cache = new ArrayList<>();
|
||||
private long length;
|
||||
private long position;
|
||||
private long start;
|
||||
|
||||
private byte[] getBlock() throws IOException {
|
||||
final long currPos = position - start;
|
||||
if (currPos < 0) {
|
||||
throw new IOException("StreamCache flushed before read position");
|
||||
}
|
||||
|
||||
long index = currPos / BLOCK_SIZE;
|
||||
|
||||
if (index >= Integer.MAX_VALUE) {
|
||||
throw new IOException("Memory cache max size exceeded");
|
||||
}
|
||||
|
||||
if (index >= cache.size()) {
|
||||
try {
|
||||
cache.add(new byte[BLOCK_SIZE]);
|
||||
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
|
||||
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
|
||||
}
|
||||
catch (OutOfMemoryError e) {
|
||||
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
//System.out.println("index: " + index);
|
||||
|
||||
return cache.get((int) index);
|
||||
}
|
||||
|
||||
public void write(final int pByte) throws IOException {
|
||||
byte[] buffer = getBlock();
|
||||
|
||||
int idx = (int) (position % BLOCK_SIZE);
|
||||
buffer[idx] = (byte) pByte;
|
||||
position++;
|
||||
|
||||
if (position > length) {
|
||||
length = position;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: OptimizeMe!!!
|
||||
@Override
|
||||
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||
byte[] buffer = getBlock();
|
||||
for (int i = 0; i < pLength; i++) {
|
||||
int index = (int) position % BLOCK_SIZE;
|
||||
if (index == 0) {
|
||||
buffer = getBlock();
|
||||
}
|
||||
buffer[index] = pBuffer[pOffset + i];
|
||||
|
||||
position++;
|
||||
}
|
||||
if (position > length) {
|
||||
length = position;
|
||||
}
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (position >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte[] buffer = getBlock();
|
||||
|
||||
int idx = (int) (position % BLOCK_SIZE);
|
||||
position++;
|
||||
|
||||
return buffer[idx] & 0xff;
|
||||
}
|
||||
|
||||
// TODO: OptimizeMe!!!
|
||||
@Override
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (position >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte[] buffer = getBlock();
|
||||
|
||||
int bufferPos = (int) (position % BLOCK_SIZE);
|
||||
|
||||
// Find maxIdx and simplify test in for-loop
|
||||
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
|
||||
|
||||
int i;
|
||||
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
|
||||
for (i = 0; i < maxLen; i++) {
|
||||
pBytes[pOffset + i] = buffer[bufferPos + i];
|
||||
}
|
||||
|
||||
position += i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public void seek(final long pPosition) throws IOException {
|
||||
if (pPosition < start) {
|
||||
throw new IOException("Seek before flush position");
|
||||
}
|
||||
position = pPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(final long pPosition) {
|
||||
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
|
||||
|
||||
for (int i = 0; i < firstPos; i++) {
|
||||
cache.remove(0);
|
||||
}
|
||||
|
||||
start = pPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
public long getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An {@code InputStream} that contains no bytes.
|
||||
* <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/NullInputStream.java#2 $
|
||||
*/
|
||||
public class NullInputStream extends InputStream {
|
||||
|
||||
/**
|
||||
* Creates a {@code NullInputStream}.
|
||||
*/
|
||||
public NullInputStream() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code -1} (EOF), always.
|
||||
*
|
||||
* @return {@code -1}
|
||||
* @throws IOException
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code 0}, always.
|
||||
*
|
||||
* @return {@code 0}
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code 0}, always.
|
||||
*
|
||||
* @return {@code 0}
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public long skip(long pOffset) throws IOException {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An {@code InputStream} that contains no bytes.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class NullInputStream extends InputStream {
|
||||
|
||||
/**
|
||||
* Creates a {@code NullInputStream}.
|
||||
*/
|
||||
public NullInputStream() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code -1} (EOF), always.
|
||||
*
|
||||
* @return {@code -1}
|
||||
* @throws IOException
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code 0}, always.
|
||||
*
|
||||
* @return {@code 0}
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code 0}, always.
|
||||
*
|
||||
* @return {@code 0}
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public long skip(long pOffset) throws IOException {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An {@code OutputStream} implementation that works as a sink.
|
||||
* <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/NullOutputStream.java#2 $
|
||||
*/
|
||||
public class NullOutputStream extends OutputStream {
|
||||
|
||||
/**
|
||||
* Creates a {@code NullOutputStream}.
|
||||
*/
|
||||
public NullOutputStream() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
public void write(int pByte) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte pBytes[]) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An {@code OutputStream} implementation that works as a sink.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class NullOutputStream extends OutputStream {
|
||||
|
||||
/**
|
||||
* Creates a {@code NullOutputStream}.
|
||||
*/
|
||||
public NullOutputStream() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
public void write(int pByte) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte pBytes[]) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||
}
|
||||
}
|
||||
@@ -1,241 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p/>
|
||||
*
|
||||
* @see java.io.RandomAccessFile
|
||||
*
|
||||
* @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/io/RandomAccessStream.java#3 $
|
||||
*/
|
||||
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
|
||||
// TODO: Use a RandomAcceessFile as backing in impl, probably
|
||||
// TODO: Create an in-memory implementation too?
|
||||
// TODO: Package private SeekableDelegate?
|
||||
|
||||
// TODO: Both read and write must update stream position
|
||||
//private int position = -1;
|
||||
|
||||
/** This random access stream, wrapped in an {@code InputStream} */
|
||||
SeekableInputStream inputView = null;
|
||||
/** This random access stream, wrapped in an {@code OutputStream} */
|
||||
SeekableOutputStream outputView = null;
|
||||
|
||||
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
|
||||
public int read() throws IOException {
|
||||
try {
|
||||
return readByte() & 0xff;
|
||||
}
|
||||
catch (EOFException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException("bytes == null");
|
||||
}
|
||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Special case, allready at EOF
|
||||
int c = read();
|
||||
if (c == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Otherwise, read as many as bytes as possible
|
||||
pBytes[pOffset] = (byte) c;
|
||||
|
||||
int i = 1;
|
||||
try {
|
||||
for (; i < pLength; i++) {
|
||||
c = read();
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
pBytes[pOffset + i] = (byte) c;
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore exception, just return length
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input view of this {@code RandomAccessStream}.
|
||||
* Invoking this method several times, will return the same object.
|
||||
* <p/>
|
||||
* <em>Note that read access is NOT synchronized.</em>
|
||||
*
|
||||
* @return a {@code SeekableInputStream} reading from this stream
|
||||
*/
|
||||
public final SeekableInputStream asInputStream() {
|
||||
if (inputView == null) {
|
||||
inputView = new InputStreamView(this);
|
||||
}
|
||||
return inputView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an output view of this {@code RandomAccessStream}.
|
||||
* Invoking this method several times, will return the same object.
|
||||
* <p/>
|
||||
* <em>Note that write access is NOT synchronized.</em>
|
||||
*
|
||||
* @return a {@code SeekableOutputStream} writing to this stream
|
||||
*/
|
||||
public final SeekableOutputStream asOutputStream() {
|
||||
if (outputView == null) {
|
||||
outputView = new OutputStreamView(this);
|
||||
}
|
||||
return outputView;
|
||||
}
|
||||
|
||||
static final class InputStreamView extends SeekableInputStream {
|
||||
// TODO: Consider adding synchonization (on stream) for all operations
|
||||
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||
// - Or should it rather just work on the views?
|
||||
// - Allow multiple views?
|
||||
|
||||
final private RandomAccessStream mStream;
|
||||
|
||||
public InputStreamView(RandomAccessStream pStream) {
|
||||
if (pStream == null) {
|
||||
throw new IllegalArgumentException("stream == null");
|
||||
}
|
||||
mStream = pStream;
|
||||
}
|
||||
|
||||
public boolean isCached() {
|
||||
return mStream.isCached();
|
||||
}
|
||||
|
||||
public boolean isCachedFile() {
|
||||
return mStream.isCachedFile();
|
||||
}
|
||||
|
||||
public boolean isCachedMemory() {
|
||||
return mStream.isCachedMemory();
|
||||
}
|
||||
|
||||
protected void closeImpl() throws IOException {
|
||||
mStream.close();
|
||||
}
|
||||
|
||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
||||
mStream.flushBefore(pPosition);
|
||||
}
|
||||
|
||||
protected void seekImpl(long pPosition) throws IOException {
|
||||
mStream.seek(pPosition);
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return mStream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||
// - Or should it rather just work on the views?
|
||||
// - Allow multiple views?
|
||||
|
||||
final private RandomAccessStream mStream;
|
||||
|
||||
public OutputStreamView(RandomAccessStream pStream) {
|
||||
if (pStream == null) {
|
||||
throw new IllegalArgumentException("stream == null");
|
||||
}
|
||||
mStream = pStream;
|
||||
}
|
||||
|
||||
public boolean isCached() {
|
||||
return mStream.isCached();
|
||||
}
|
||||
|
||||
public boolean isCachedFile() {
|
||||
return mStream.isCachedFile();
|
||||
}
|
||||
|
||||
public boolean isCachedMemory() {
|
||||
return mStream.isCachedMemory();
|
||||
}
|
||||
|
||||
protected void closeImpl() throws IOException {
|
||||
mStream.close();
|
||||
}
|
||||
|
||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
||||
mStream.flushBefore(pPosition);
|
||||
}
|
||||
|
||||
protected void seekImpl(long pPosition) throws IOException {
|
||||
mStream.seek(pPosition);
|
||||
}
|
||||
|
||||
public void write(int pByte) throws IOException {
|
||||
mStream.write(pByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||
mStream.write(pBytes, pOffset, pLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @see java.io.RandomAccessFile
|
||||
*
|
||||
* @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/io/RandomAccessStream.java#3 $
|
||||
*/
|
||||
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
|
||||
// TODO: Use a RandomAcceessFile as backing in impl, probably
|
||||
// TODO: Create an in-memory implementation too?
|
||||
// TODO: Package private SeekableDelegate?
|
||||
|
||||
// TODO: Both read and write must update stream position
|
||||
//private int position = -1;
|
||||
|
||||
/** This random access stream, wrapped in an {@code InputStream} */
|
||||
SeekableInputStream inputView = null;
|
||||
/** This random access stream, wrapped in an {@code OutputStream} */
|
||||
SeekableOutputStream outputView = null;
|
||||
|
||||
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
|
||||
public int read() throws IOException {
|
||||
try {
|
||||
return readByte() & 0xff;
|
||||
}
|
||||
catch (EOFException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException("bytes == null");
|
||||
}
|
||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Special case, allready at EOF
|
||||
int c = read();
|
||||
if (c == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Otherwise, read as many as bytes as possible
|
||||
pBytes[pOffset] = (byte) c;
|
||||
|
||||
int i = 1;
|
||||
try {
|
||||
for (; i < pLength; i++) {
|
||||
c = read();
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
pBytes[pOffset + i] = (byte) c;
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore exception, just return length
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input view of this {@code RandomAccessStream}.
|
||||
* Invoking this method several times, will return the same object.
|
||||
* <p>
|
||||
* <em>Note that read access is NOT synchronized.</em>
|
||||
* </p>
|
||||
*
|
||||
* @return a {@code SeekableInputStream} reading from this stream
|
||||
*/
|
||||
public final SeekableInputStream asInputStream() {
|
||||
if (inputView == null) {
|
||||
inputView = new InputStreamView(this);
|
||||
}
|
||||
return inputView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an output view of this {@code RandomAccessStream}.
|
||||
* Invoking this method several times, will return the same object.
|
||||
* <p>
|
||||
* <em>Note that write access is NOT synchronized.</em>
|
||||
* </p>
|
||||
*
|
||||
* @return a {@code SeekableOutputStream} writing to this stream
|
||||
*/
|
||||
public final SeekableOutputStream asOutputStream() {
|
||||
if (outputView == null) {
|
||||
outputView = new OutputStreamView(this);
|
||||
}
|
||||
return outputView;
|
||||
}
|
||||
|
||||
static final class InputStreamView extends SeekableInputStream {
|
||||
// TODO: Consider adding synchonization (on stream) for all operations
|
||||
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||
// - Or should it rather just work on the views?
|
||||
// - Allow multiple views?
|
||||
|
||||
final private RandomAccessStream mStream;
|
||||
|
||||
public InputStreamView(RandomAccessStream pStream) {
|
||||
if (pStream == null) {
|
||||
throw new IllegalArgumentException("stream == null");
|
||||
}
|
||||
mStream = pStream;
|
||||
}
|
||||
|
||||
public boolean isCached() {
|
||||
return mStream.isCached();
|
||||
}
|
||||
|
||||
public boolean isCachedFile() {
|
||||
return mStream.isCachedFile();
|
||||
}
|
||||
|
||||
public boolean isCachedMemory() {
|
||||
return mStream.isCachedMemory();
|
||||
}
|
||||
|
||||
protected void closeImpl() throws IOException {
|
||||
mStream.close();
|
||||
}
|
||||
|
||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
||||
mStream.flushBefore(pPosition);
|
||||
}
|
||||
|
||||
protected void seekImpl(long pPosition) throws IOException {
|
||||
mStream.seek(pPosition);
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return mStream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||
// - Or should it rather just work on the views?
|
||||
// - Allow multiple views?
|
||||
|
||||
final private RandomAccessStream mStream;
|
||||
|
||||
public OutputStreamView(RandomAccessStream pStream) {
|
||||
if (pStream == null) {
|
||||
throw new IllegalArgumentException("stream == null");
|
||||
}
|
||||
mStream = pStream;
|
||||
}
|
||||
|
||||
public boolean isCached() {
|
||||
return mStream.isCached();
|
||||
}
|
||||
|
||||
public boolean isCachedFile() {
|
||||
return mStream.isCachedFile();
|
||||
}
|
||||
|
||||
public boolean isCachedMemory() {
|
||||
return mStream.isCachedMemory();
|
||||
}
|
||||
|
||||
protected void closeImpl() throws IOException {
|
||||
mStream.close();
|
||||
}
|
||||
|
||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
||||
mStream.flushBefore(pPosition);
|
||||
}
|
||||
|
||||
protected void seekImpl(long pPosition) throws IOException {
|
||||
mStream.seek(pPosition);
|
||||
}
|
||||
|
||||
public void write(int pByte) throws IOException {
|
||||
mStream.write(pByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||
mStream.write(pBytes, pOffset, pLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,186 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for seekable streams.
|
||||
* <p/>
|
||||
* @see SeekableInputStream
|
||||
* @see SeekableOutputStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface Seekable {
|
||||
|
||||
/**
|
||||
* Returns the current byte position of the stream. The next read will take
|
||||
* place starting at this offset.
|
||||
*
|
||||
* @return a {@code long} containing the position of the stream.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
long getStreamPosition() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the current stream position to the desired location.
|
||||
* The next read will occur at this location.
|
||||
* <p/>
|
||||
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
|
||||
* than the flushed position (as returned by {@link #getFlushedPosition()}).
|
||||
* <p/>
|
||||
* 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.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
|
||||
* the flushed position.
|
||||
* @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}
|
||||
* streams upport marking. Additionally, calls to {@code mark} and
|
||||
* {@code reset} may be nested arbitrarily.
|
||||
* <p/>
|
||||
* Unlike the {@code mark} methods declared by the {@code Reader} or
|
||||
* {@code InputStream}
|
||||
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
|
||||
* of data may be read following the call to {@code mark}.
|
||||
*/
|
||||
void mark();
|
||||
|
||||
/**
|
||||
* Returns the file pointer to its previous position,
|
||||
* at the time of the most recent unmatched call to mark.
|
||||
* <p/>
|
||||
* Calls to reset without a corresponding call to mark will either:
|
||||
* <ul>
|
||||
* <li>throw an {@code IOException}</li>
|
||||
* <li>or, reset to the beginning of the stream.</li>
|
||||
* </ul>
|
||||
* An {@code IOException} will be thrown if the previous marked position
|
||||
* lies in the discarded portion of the stream.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
void reset() throws IOException;
|
||||
|
||||
/**
|
||||
* Discards the initial portion of the stream prior to the indicated
|
||||
* postion. Attempting to seek to an offset within the flushed portion of
|
||||
* the stream will result in an {@code IndexOutOfBoundsException}.
|
||||
* <p/>
|
||||
* 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.
|
||||
*
|
||||
* @param pPosition a long containing the length of the file prefix that
|
||||
* may be flushed.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the
|
||||
* flushed portion of the stream or past the current stream position.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
void flushBefore(long pPosition) throws IOException;
|
||||
|
||||
/**
|
||||
* Discards the initial position of the stream prior to the current stream
|
||||
* position. Equivalent to {@code flushBefore(getStreamPosition())}.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
void flush() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the earliest position in the stream to which seeking may be
|
||||
* performed. The returned value will be the maximum of all values passed
|
||||
* into previous calls to {@code flushBefore}.
|
||||
*
|
||||
* @return the earliest legal position for seeking, as a {@code long}.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
long getFlushedPosition() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns true if this {@code Seekable} stream caches data itself in order
|
||||
* to allow seeking backwards. Applications may consult this in order to
|
||||
* decide how frequently, or whether, to flush in order to conserve cache
|
||||
* resources.
|
||||
*
|
||||
* @return {@code true} if this {@code Seekable} caches data.
|
||||
* @see #isCachedMemory()
|
||||
* @see #isCachedFile()
|
||||
*/
|
||||
boolean isCached();
|
||||
|
||||
/**
|
||||
* Returns true if this {@code Seekable} stream caches data itself in order
|
||||
* to allow seeking backwards, and the cache is kept in main memory.
|
||||
* Applications may consult this in order to decide how frequently, or
|
||||
* whether, to flush in order to conserve cache resources.
|
||||
*
|
||||
* @return {@code true} if this {@code Seekable} caches data in main
|
||||
* memory.
|
||||
* @see #isCached()
|
||||
* @see #isCachedFile()
|
||||
*/
|
||||
boolean isCachedMemory();
|
||||
|
||||
/**
|
||||
* Returns true if this {@code Seekable} stream caches data itself in
|
||||
* order to allow seeking backwards, and the cache is kept in a
|
||||
* temporary file.
|
||||
* Applications may consult this in order to decide how frequently,
|
||||
* or whether, to flush in order to conserve cache resources.
|
||||
*
|
||||
* @return {@code true} if this {@code Seekable} caches data in a
|
||||
* temporary file.
|
||||
* @see #isCached
|
||||
* @see #isCachedMemory
|
||||
*/
|
||||
boolean isCachedFile();
|
||||
|
||||
/**
|
||||
* Closes the stream.
|
||||
*
|
||||
* @throws java.io.IOException if the stream can't be closed.
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for seekable streams.
|
||||
*
|
||||
* @see SeekableInputStream
|
||||
* @see SeekableOutputStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface Seekable {
|
||||
|
||||
/**
|
||||
* Returns the current byte position of the stream. The next read will take
|
||||
* place starting at this offset.
|
||||
*
|
||||
* @return a {@code long} containing the position of the stream.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
long getStreamPosition() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the current stream position to the desired location.
|
||||
* The next read will occur at this location.
|
||||
* <p>
|
||||
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
|
||||
* 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}
|
||||
* will be thrown only if a read is performed.
|
||||
* </p>
|
||||
*
|
||||
* @param pPosition a long containing the desired file pointer position.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
|
||||
* the flushed position.
|
||||
* @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}
|
||||
* streams upport marking. Additionally, calls to {@code mark} and
|
||||
* {@code reset} may be nested arbitrarily.
|
||||
* <p>
|
||||
* Unlike the {@code mark} methods declared by the {@code Reader} or
|
||||
* {@code InputStream}
|
||||
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
|
||||
* of data may be read following the call to {@code mark}.
|
||||
* </p>
|
||||
*/
|
||||
void mark();
|
||||
|
||||
/**
|
||||
* Returns the file pointer to its previous position,
|
||||
* at the time of the most recent unmatched call to mark.
|
||||
* <p>
|
||||
* Calls to reset without a corresponding call to mark will either:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>throw an {@code IOException}</li>
|
||||
* <li>or, reset to the beginning of the stream.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* An {@code IOException} will be thrown if the previous marked position
|
||||
* lies in the discarded portion of the stream.
|
||||
* </p>
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
void reset() throws IOException;
|
||||
|
||||
/**
|
||||
* Discards the initial portion of the stream prior to the indicated
|
||||
* postion. Attempting to seek to an offset within the flushed portion of
|
||||
* the stream will result in an {@code IndexOutOfBoundsException}.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @param pPosition a long containing the length of the file prefix that
|
||||
* may be flushed.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the
|
||||
* flushed portion of the stream or past the current stream position.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
void flushBefore(long pPosition) throws IOException;
|
||||
|
||||
/**
|
||||
* Discards the initial position of the stream prior to the current stream
|
||||
* position. Equivalent to {@code flushBefore(getStreamPosition())}.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
void flush() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the earliest position in the stream to which seeking may be
|
||||
* performed. The returned value will be the maximum of all values passed
|
||||
* into previous calls to {@code flushBefore}.
|
||||
*
|
||||
* @return the earliest legal position for seeking, as a {@code long}.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
long getFlushedPosition() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns true if this {@code Seekable} stream caches data itself in order
|
||||
* to allow seeking backwards. Applications may consult this in order to
|
||||
* decide how frequently, or whether, to flush in order to conserve cache
|
||||
* resources.
|
||||
*
|
||||
* @return {@code true} if this {@code Seekable} caches data.
|
||||
* @see #isCachedMemory()
|
||||
* @see #isCachedFile()
|
||||
*/
|
||||
boolean isCached();
|
||||
|
||||
/**
|
||||
* Returns true if this {@code Seekable} stream caches data itself in order
|
||||
* to allow seeking backwards, and the cache is kept in main memory.
|
||||
* Applications may consult this in order to decide how frequently, or
|
||||
* whether, to flush in order to conserve cache resources.
|
||||
*
|
||||
* @return {@code true} if this {@code Seekable} caches data in main
|
||||
* memory.
|
||||
* @see #isCached()
|
||||
* @see #isCachedFile()
|
||||
*/
|
||||
boolean isCachedMemory();
|
||||
|
||||
/**
|
||||
* Returns true if this {@code Seekable} stream caches data itself in
|
||||
* order to allow seeking backwards, and the cache is kept in a
|
||||
* temporary file.
|
||||
* Applications may consult this in order to decide how frequently,
|
||||
* or whether, to flush in order to conserve cache resources.
|
||||
*
|
||||
* @return {@code true} if this {@code Seekable} caches data in a
|
||||
* temporary file.
|
||||
* @see #isCached
|
||||
* @see #isCachedMemory
|
||||
*/
|
||||
boolean isCachedFile();
|
||||
|
||||
/**
|
||||
* Closes the stream.
|
||||
*
|
||||
* @throws java.io.IOException if the stream can't be closed.
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,238 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
|
||||
* <p/>
|
||||
* @see SeekableOutputStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public abstract class SeekableInputStream extends InputStream implements Seekable {
|
||||
|
||||
// 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
|
||||
// updated from the read/read/read methods...
|
||||
|
||||
/** The stream position in this stream */
|
||||
long position;
|
||||
long flushedPosition;
|
||||
boolean closed;
|
||||
|
||||
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||
|
||||
/// InputStream overrides
|
||||
@Override
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented using {@code seek(currentPos + pLength)}.
|
||||
*
|
||||
* @param pLength the number of bytes to skip
|
||||
* @return the actual number of bytes skipped (may be equal to or less
|
||||
* than {@code pLength})
|
||||
*
|
||||
* @throws IOException if an I/O exception occurs during skip
|
||||
*/
|
||||
@Override
|
||||
public final long skip(final long pLength) throws IOException {
|
||||
long pos = position;
|
||||
long wantedPosition = pos + pLength;
|
||||
if (wantedPosition < flushedPosition) {
|
||||
throw new IOException("position < flushedPosition");
|
||||
}
|
||||
|
||||
// Stop at stream length for compatibility, even though it might be allowed
|
||||
// to seek past end of stream
|
||||
int available = available();
|
||||
if (available > 0) {
|
||||
seek(Math.min(wantedPosition, pos + available));
|
||||
}
|
||||
// TODO: Add optimization for streams with known length!
|
||||
else {
|
||||
// Slow mode...
|
||||
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
|
||||
while (toSkip > 0 && read() >= 0) {
|
||||
toSkip--;
|
||||
}
|
||||
}
|
||||
|
||||
return position - pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void mark(int pLimit) {
|
||||
mark();
|
||||
|
||||
// TODO: We don't really need to do this.. Is it a good idea?
|
||||
try {
|
||||
flushBefore(Math.max(position - pLimit, flushedPosition));
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore, as it's not really critical
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true}, as marking is always supported.
|
||||
*
|
||||
* @return {@code true}.
|
||||
*/
|
||||
@Override
|
||||
public final boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Seekable implementation
|
||||
public final void seek(long pPosition) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||
// but it's kind of inconsistent with reset that throws IOException...
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||
}
|
||||
|
||||
seekImpl(pPosition);
|
||||
position = pPosition;
|
||||
}
|
||||
|
||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||
|
||||
public final void mark() {
|
||||
markedPositions.push(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void reset() throws IOException {
|
||||
checkOpen();
|
||||
if (!markedPositions.isEmpty()) {
|
||||
long newPos = markedPositions.pop();
|
||||
|
||||
// NOTE: This is correct according to javax.imageio (IOException),
|
||||
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
|
||||
if (newPos < flushedPosition) {
|
||||
throw new IOException("Previous marked position has been discarded");
|
||||
}
|
||||
|
||||
seek(newPos);
|
||||
}
|
||||
else {
|
||||
// TODO: To iron out some wrinkles due to conflicting contracts
|
||||
// (InputStream and Seekable both declare reset),
|
||||
// we might need to reset to the last marked position instead..
|
||||
// However, that becomes REALLY confusing if that position is after
|
||||
// the current position...
|
||||
seek(0);
|
||||
}
|
||||
}
|
||||
|
||||
public final void flushBefore(long pPosition) throws IOException {
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||
}
|
||||
if (pPosition > getStreamPosition()) {
|
||||
throw new IndexOutOfBoundsException("position > stream position");
|
||||
}
|
||||
checkOpen();
|
||||
flushBeforeImpl(pPosition);
|
||||
flushedPosition = pPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the initial portion of the stream prior to the indicated postion.
|
||||
*
|
||||
* @param pPosition the position to flush to
|
||||
* @throws IOException if an I/O exception occurs during the flush operation
|
||||
*
|
||||
* @see #flushBefore(long)
|
||||
*/
|
||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||
|
||||
public final void flush() throws IOException {
|
||||
flushBefore(flushedPosition);
|
||||
}
|
||||
|
||||
public final long getFlushedPosition() throws IOException {
|
||||
checkOpen();
|
||||
return flushedPosition;
|
||||
}
|
||||
|
||||
public final long getStreamPosition() throws IOException {
|
||||
checkOpen();
|
||||
return position;
|
||||
}
|
||||
|
||||
protected final void checkOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
checkOpen();
|
||||
closed = true;
|
||||
closeImpl();
|
||||
}
|
||||
|
||||
protected abstract void closeImpl() throws IOException;
|
||||
|
||||
/**
|
||||
* Finalizes this object prior to garbage collection. The
|
||||
* {@code close} method is called to close any open input
|
||||
* source. This method should not be called from application
|
||||
* code.
|
||||
*
|
||||
* @exception Throwable if an error occurs during superclass
|
||||
* finalization.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (!closed) {
|
||||
try {
|
||||
close();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignroe
|
||||
}
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
|
||||
*
|
||||
* @see SeekableOutputStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public abstract class SeekableInputStream extends InputStream implements Seekable {
|
||||
|
||||
// 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
|
||||
// updated from the read/read/read methods...
|
||||
|
||||
/** The stream position in this stream */
|
||||
long position;
|
||||
long flushedPosition;
|
||||
boolean closed;
|
||||
|
||||
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||
|
||||
/// InputStream overrides
|
||||
@Override
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented using {@code seek(currentPos + pLength)}.
|
||||
*
|
||||
* @param pLength the number of bytes to skip
|
||||
* @return the actual number of bytes skipped (may be equal to or less
|
||||
* than {@code pLength})
|
||||
*
|
||||
* @throws IOException if an I/O exception occurs during skip
|
||||
*/
|
||||
@Override
|
||||
public final long skip(final long pLength) throws IOException {
|
||||
long pos = position;
|
||||
long wantedPosition = pos + pLength;
|
||||
if (wantedPosition < flushedPosition) {
|
||||
throw new IOException("position < flushedPosition");
|
||||
}
|
||||
|
||||
// Stop at stream length for compatibility, even though it might be allowed
|
||||
// to seek past end of stream
|
||||
int available = available();
|
||||
if (available > 0) {
|
||||
seek(Math.min(wantedPosition, pos + available));
|
||||
}
|
||||
// TODO: Add optimization for streams with known length!
|
||||
else {
|
||||
// Slow mode...
|
||||
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
|
||||
while (toSkip > 0 && read() >= 0) {
|
||||
toSkip--;
|
||||
}
|
||||
}
|
||||
|
||||
return position - pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void mark(int pLimit) {
|
||||
mark();
|
||||
|
||||
// TODO: We don't really need to do this.. Is it a good idea?
|
||||
try {
|
||||
flushBefore(Math.max(position - pLimit, flushedPosition));
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore, as it's not really critical
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true}, as marking is always supported.
|
||||
*
|
||||
* @return {@code true}.
|
||||
*/
|
||||
@Override
|
||||
public final boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Seekable implementation
|
||||
public final void seek(long pPosition) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||
// but it's kind of inconsistent with reset that throws IOException...
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||
}
|
||||
|
||||
seekImpl(pPosition);
|
||||
position = pPosition;
|
||||
}
|
||||
|
||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||
|
||||
public final void mark() {
|
||||
markedPositions.push(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void reset() throws IOException {
|
||||
checkOpen();
|
||||
if (!markedPositions.isEmpty()) {
|
||||
long newPos = markedPositions.pop();
|
||||
|
||||
// NOTE: This is correct according to javax.imageio (IOException),
|
||||
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
|
||||
if (newPos < flushedPosition) {
|
||||
throw new IOException("Previous marked position has been discarded");
|
||||
}
|
||||
|
||||
seek(newPos);
|
||||
}
|
||||
else {
|
||||
// TODO: To iron out some wrinkles due to conflicting contracts
|
||||
// (InputStream and Seekable both declare reset),
|
||||
// we might need to reset to the last marked position instead..
|
||||
// However, that becomes REALLY confusing if that position is after
|
||||
// the current position...
|
||||
seek(0);
|
||||
}
|
||||
}
|
||||
|
||||
public final void flushBefore(long pPosition) throws IOException {
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||
}
|
||||
if (pPosition > getStreamPosition()) {
|
||||
throw new IndexOutOfBoundsException("position > stream position");
|
||||
}
|
||||
checkOpen();
|
||||
flushBeforeImpl(pPosition);
|
||||
flushedPosition = pPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the initial portion of the stream prior to the indicated postion.
|
||||
*
|
||||
* @param pPosition the position to flush to
|
||||
* @throws IOException if an I/O exception occurs during the flush operation
|
||||
*
|
||||
* @see #flushBefore(long)
|
||||
*/
|
||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||
|
||||
public final void flush() throws IOException {
|
||||
flushBefore(flushedPosition);
|
||||
}
|
||||
|
||||
public final long getFlushedPosition() throws IOException {
|
||||
checkOpen();
|
||||
return flushedPosition;
|
||||
}
|
||||
|
||||
public final long getStreamPosition() throws IOException {
|
||||
checkOpen();
|
||||
return position;
|
||||
}
|
||||
|
||||
protected final void checkOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
checkOpen();
|
||||
closed = true;
|
||||
closeImpl();
|
||||
}
|
||||
|
||||
protected abstract void closeImpl() throws IOException;
|
||||
|
||||
/**
|
||||
* Finalizes this object prior to garbage collection. The
|
||||
* {@code close} method is called to close any open input
|
||||
* source. This method should not be called from application
|
||||
* code.
|
||||
*
|
||||
* @exception Throwable if an error occurs during superclass
|
||||
* finalization.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (!closed) {
|
||||
try {
|
||||
close();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignroe
|
||||
}
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@code OutputStream}s implementing the
|
||||
* {@code Seekable} interface.
|
||||
* <p/>
|
||||
* @see SeekableInputStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
|
||||
// TODO: Implement
|
||||
long position;
|
||||
long flushedPosition;
|
||||
boolean closed;
|
||||
|
||||
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||
|
||||
/// Outputstream overrides
|
||||
@Override
|
||||
public final void write(byte pBytes[]) throws IOException {
|
||||
write(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||
}
|
||||
|
||||
/// Seekable implementation
|
||||
// TODO: This is common behaviour/implementation with SeekableInputStream,
|
||||
// probably a good idea to extract a delegate..?
|
||||
public final void seek(long pPosition) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||
// but it's inconsistent with reset that throws IOException...
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||
}
|
||||
|
||||
seekImpl(pPosition);
|
||||
position = pPosition;
|
||||
}
|
||||
|
||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||
|
||||
public final void mark() {
|
||||
markedPositions.push(position);
|
||||
}
|
||||
|
||||
public final void reset() throws IOException {
|
||||
checkOpen();
|
||||
if (!markedPositions.isEmpty()) {
|
||||
long newPos = markedPositions.pop();
|
||||
|
||||
// TODO: This is correct according to javax.imageio (IOException),
|
||||
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
|
||||
if (newPos < flushedPosition) {
|
||||
throw new IOException("Previous marked position has been discarded!");
|
||||
}
|
||||
|
||||
seek(newPos);
|
||||
}
|
||||
}
|
||||
|
||||
public final void flushBefore(long pPosition) throws IOException {
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||
}
|
||||
if (pPosition > getStreamPosition()) {
|
||||
throw new IndexOutOfBoundsException("position > getStreamPosition()!");
|
||||
}
|
||||
checkOpen();
|
||||
flushBeforeImpl(pPosition);
|
||||
flushedPosition = pPosition;
|
||||
}
|
||||
|
||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||
|
||||
@Override
|
||||
public final void flush() throws IOException {
|
||||
flushBefore(flushedPosition);
|
||||
}
|
||||
|
||||
public final long getFlushedPosition() throws IOException {
|
||||
checkOpen();
|
||||
return flushedPosition;
|
||||
}
|
||||
|
||||
public final long getStreamPosition() throws IOException {
|
||||
checkOpen();
|
||||
return position;
|
||||
}
|
||||
|
||||
protected final void checkOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
checkOpen();
|
||||
closed = true;
|
||||
closeImpl();
|
||||
}
|
||||
|
||||
protected abstract void closeImpl() throws IOException;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@code OutputStream}s implementing the
|
||||
* {@code Seekable} interface.
|
||||
*
|
||||
* @see SeekableInputStream
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
|
||||
// TODO: Implement
|
||||
long position;
|
||||
long flushedPosition;
|
||||
boolean closed;
|
||||
|
||||
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||
|
||||
/// Outputstream overrides
|
||||
@Override
|
||||
public final void write(byte pBytes[]) throws IOException {
|
||||
write(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||
}
|
||||
|
||||
/// Seekable implementation
|
||||
// TODO: This is common behaviour/implementation with SeekableInputStream,
|
||||
// probably a good idea to extract a delegate..?
|
||||
public final void seek(long pPosition) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||
// but it's inconsistent with reset that throws IOException...
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||
}
|
||||
|
||||
seekImpl(pPosition);
|
||||
position = pPosition;
|
||||
}
|
||||
|
||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||
|
||||
public final void mark() {
|
||||
markedPositions.push(position);
|
||||
}
|
||||
|
||||
public final void reset() throws IOException {
|
||||
checkOpen();
|
||||
if (!markedPositions.isEmpty()) {
|
||||
long newPos = markedPositions.pop();
|
||||
|
||||
// TODO: This is correct according to javax.imageio (IOException),
|
||||
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
|
||||
if (newPos < flushedPosition) {
|
||||
throw new IOException("Previous marked position has been discarded!");
|
||||
}
|
||||
|
||||
seek(newPos);
|
||||
}
|
||||
}
|
||||
|
||||
public final void flushBefore(long pPosition) throws IOException {
|
||||
if (pPosition < flushedPosition) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||
}
|
||||
if (pPosition > getStreamPosition()) {
|
||||
throw new IndexOutOfBoundsException("position > getStreamPosition()!");
|
||||
}
|
||||
checkOpen();
|
||||
flushBeforeImpl(pPosition);
|
||||
flushedPosition = pPosition;
|
||||
}
|
||||
|
||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||
|
||||
@Override
|
||||
public final void flush() throws IOException {
|
||||
flushBefore(flushedPosition);
|
||||
}
|
||||
|
||||
public final long getFlushedPosition() throws IOException {
|
||||
checkOpen();
|
||||
return flushedPosition;
|
||||
}
|
||||
|
||||
public final long getStreamPosition() throws IOException {
|
||||
checkOpen();
|
||||
return position;
|
||||
}
|
||||
|
||||
protected final void checkOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
checkOpen();
|
||||
closed = true;
|
||||
closeImpl();
|
||||
}
|
||||
|
||||
protected abstract void closeImpl() throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,189 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* StringArrayReader
|
||||
* <p/>
|
||||
*
|
||||
* @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/io/StringArrayReader.java#2 $
|
||||
*/
|
||||
public class StringArrayReader extends StringReader {
|
||||
|
||||
private StringReader current;
|
||||
private String[] strings;
|
||||
protected final Object finalLock;
|
||||
private int currentSting;
|
||||
private int markedString;
|
||||
private int mark;
|
||||
private int next;
|
||||
|
||||
/**
|
||||
* Create a new string array reader.
|
||||
*
|
||||
* @param pStrings {@code String}s providing the character stream.
|
||||
*/
|
||||
public StringArrayReader(final String[] pStrings) {
|
||||
super("");
|
||||
|
||||
Validate.notNull(pStrings, "strings");
|
||||
|
||||
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
|
||||
// reference can't change, only it's elements
|
||||
|
||||
strings = pStrings.clone(); // Defensive copy for content
|
||||
nextReader();
|
||||
}
|
||||
|
||||
protected final Reader nextReader() {
|
||||
if (currentSting >= strings.length) {
|
||||
current = new EmptyReader();
|
||||
}
|
||||
else {
|
||||
current = new StringReader(strings[currentSting++]);
|
||||
}
|
||||
|
||||
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
|
||||
next = 0;
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to make sure that the stream has not been closed
|
||||
*
|
||||
* @throws IOException if the stream is closed
|
||||
*/
|
||||
protected final void ensureOpen() throws IOException {
|
||||
if (strings == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
super.close();
|
||||
strings = null;
|
||||
current.close();
|
||||
}
|
||||
|
||||
public void mark(int pReadLimit) throws IOException {
|
||||
if (pReadLimit < 0){
|
||||
throw new IllegalArgumentException("Read limit < 0");
|
||||
}
|
||||
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
mark = next;
|
||||
markedString = currentSting;
|
||||
|
||||
current.mark(pReadLimit);
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
|
||||
if (currentSting != markedString) {
|
||||
currentSting = markedString - 1;
|
||||
nextReader();
|
||||
current.skip(mark);
|
||||
}
|
||||
else {
|
||||
current.reset();
|
||||
}
|
||||
|
||||
next = mark;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read();
|
||||
|
||||
if (read < 0 && currentSting < strings.length) {
|
||||
nextReader();
|
||||
return read(); // In case of empty strings
|
||||
}
|
||||
|
||||
next++;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read(pBuffer, pOffset, pLength);
|
||||
|
||||
if (read < 0 && currentSting < strings.length) {
|
||||
nextReader();
|
||||
return read(pBuffer, pOffset, pLength); // In case of empty strings
|
||||
}
|
||||
|
||||
next += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean ready() throws IOException {
|
||||
return current.ready();
|
||||
}
|
||||
|
||||
public long skip(long pChars) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
long skipped = current.skip(pChars);
|
||||
|
||||
if (skipped == 0 && currentSting < strings.length) {
|
||||
nextReader();
|
||||
return skip(pChars);
|
||||
}
|
||||
|
||||
next += skipped;
|
||||
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* StringArrayReader
|
||||
*
|
||||
* @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/io/StringArrayReader.java#2 $
|
||||
*/
|
||||
public class StringArrayReader extends StringReader {
|
||||
|
||||
private StringReader current;
|
||||
private String[] strings;
|
||||
protected final Object finalLock;
|
||||
private int currentSting;
|
||||
private int markedString;
|
||||
private int mark;
|
||||
private int next;
|
||||
|
||||
/**
|
||||
* Create a new string array reader.
|
||||
*
|
||||
* @param pStrings {@code String}s providing the character stream.
|
||||
*/
|
||||
public StringArrayReader(final String[] pStrings) {
|
||||
super("");
|
||||
|
||||
Validate.notNull(pStrings, "strings");
|
||||
|
||||
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
|
||||
// reference can't change, only it's elements
|
||||
|
||||
strings = pStrings.clone(); // Defensive copy for content
|
||||
nextReader();
|
||||
}
|
||||
|
||||
protected final Reader nextReader() {
|
||||
if (currentSting >= strings.length) {
|
||||
current = new EmptyReader();
|
||||
}
|
||||
else {
|
||||
current = new StringReader(strings[currentSting++]);
|
||||
}
|
||||
|
||||
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
|
||||
next = 0;
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to make sure that the stream has not been closed
|
||||
*
|
||||
* @throws IOException if the stream is closed
|
||||
*/
|
||||
protected final void ensureOpen() throws IOException {
|
||||
if (strings == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
super.close();
|
||||
strings = null;
|
||||
current.close();
|
||||
}
|
||||
|
||||
public void mark(int pReadLimit) throws IOException {
|
||||
if (pReadLimit < 0){
|
||||
throw new IllegalArgumentException("Read limit < 0");
|
||||
}
|
||||
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
mark = next;
|
||||
markedString = currentSting;
|
||||
|
||||
current.mark(pReadLimit);
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
ensureOpen();
|
||||
|
||||
if (currentSting != markedString) {
|
||||
currentSting = markedString - 1;
|
||||
nextReader();
|
||||
current.skip(mark);
|
||||
}
|
||||
else {
|
||||
current.reset();
|
||||
}
|
||||
|
||||
next = mark;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read();
|
||||
|
||||
if (read < 0 && currentSting < strings.length) {
|
||||
nextReader();
|
||||
return read(); // In case of empty strings
|
||||
}
|
||||
|
||||
next++;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
int read = current.read(pBuffer, pOffset, pLength);
|
||||
|
||||
if (read < 0 && currentSting < strings.length) {
|
||||
nextReader();
|
||||
return read(pBuffer, pOffset, pLength); // In case of empty strings
|
||||
}
|
||||
|
||||
next += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean ready() throws IOException {
|
||||
return current.ready();
|
||||
}
|
||||
|
||||
public long skip(long pChars) throws IOException {
|
||||
synchronized (finalLock) {
|
||||
long skipped = current.skip(pChars);
|
||||
|
||||
if (skipped == 0 && currentSting < strings.length) {
|
||||
nextReader();
|
||||
return skip(pChars);
|
||||
}
|
||||
|
||||
next += skipped;
|
||||
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,137 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An {@code InputStream} reading up to a specified number of bytes from an
|
||||
* underlying stream.
|
||||
* <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/SubStream.java#2 $
|
||||
*/
|
||||
public final class SubStream extends FilterInputStream {
|
||||
private long bytesLeft;
|
||||
private int markLimit;
|
||||
|
||||
/**
|
||||
* Creates a {@code SubStream} of the given {@code pStream}.
|
||||
*
|
||||
* @param pStream the underlying input stream
|
||||
* @param pLength maximum number of bytes to read drom this stream
|
||||
*/
|
||||
public SubStream(final InputStream pStream, final long pLength) {
|
||||
super(Validate.notNull(pStream, "stream"));
|
||||
bytesLeft = pLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this stream as closed.
|
||||
* This implementation does <em>not</em> close the underlying stream.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// NOTE: Do not close the underlying stream
|
||||
while (bytesLeft > 0) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
skip(bytesLeft);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return (int) Math.min(super.available(), bytesLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int pReadLimit) {
|
||||
super.mark(pReadLimit);// This either succeeds or does nothing...
|
||||
markLimit = pReadLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
super.reset();// This either succeeds or throws IOException
|
||||
bytesLeft += markLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (bytesLeft-- <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return super.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
return read(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (bytesLeft <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
||||
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
||||
return read;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the maximum number of bytes we can read or skip, from this stream.
|
||||
*
|
||||
* @param pLength the requested length
|
||||
* @return the maximum number of bytes to read
|
||||
*/
|
||||
private long findMaxLen(long pLength) {
|
||||
if (bytesLeft < pLength) {
|
||||
return (int) Math.max(bytesLeft, 0);
|
||||
}
|
||||
else {
|
||||
return pLength;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long pLength) throws IOException {
|
||||
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
|
||||
bytesLeft -= skipped;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An {@code InputStream} reading up to a specified number of bytes from an
|
||||
* underlying stream.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class SubStream extends FilterInputStream {
|
||||
private long bytesLeft;
|
||||
private int markLimit;
|
||||
|
||||
/**
|
||||
* Creates a {@code SubStream} of the given {@code pStream}.
|
||||
*
|
||||
* @param pStream the underlying input stream
|
||||
* @param pLength maximum number of bytes to read drom this stream
|
||||
*/
|
||||
public SubStream(final InputStream pStream, final long pLength) {
|
||||
super(Validate.notNull(pStream, "stream"));
|
||||
bytesLeft = pLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this stream as closed.
|
||||
* This implementation does <em>not</em> close the underlying stream.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// NOTE: Do not close the underlying stream
|
||||
while (bytesLeft > 0) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
skip(bytesLeft);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return (int) Math.min(super.available(), bytesLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int pReadLimit) {
|
||||
super.mark(pReadLimit);// This either succeeds or does nothing...
|
||||
markLimit = pReadLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
super.reset();// This either succeeds or throws IOException
|
||||
bytesLeft += markLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (bytesLeft-- <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return super.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
return read(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (bytesLeft <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
||||
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
||||
return read;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the maximum number of bytes we can read or skip, from this stream.
|
||||
*
|
||||
* @param pLength the requested length
|
||||
* @return the maximum number of bytes to read
|
||||
*/
|
||||
private long findMaxLen(long pLength) {
|
||||
if (bytesLeft < pLength) {
|
||||
return (int) Math.max(bytesLeft, 0);
|
||||
}
|
||||
else {
|
||||
return pLength;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long pLength) throws IOException {
|
||||
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
|
||||
bytesLeft -= skipped;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.util.StringTokenIterator;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* UnixFileSystem
|
||||
* <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/UnixFileSystem.java#1 $
|
||||
*/
|
||||
final class UnixFileSystem extends FileSystem {
|
||||
long getFreeSpace(File pPath) {
|
||||
try {
|
||||
return getNumber(pPath, 3);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
long getTotalSpace(File pPath) {
|
||||
try {
|
||||
return getNumber(pPath, 5);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
private long getNumber(File pPath, int pIndex) throws IOException {
|
||||
// TODO: Test on other platforms
|
||||
// Tested on Mac OS X, CygWin
|
||||
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
|
||||
|
||||
String last = null;
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
last = line;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FileUtil.close(reader);
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
String blocks = null;
|
||||
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
|
||||
int count = 0;
|
||||
// We want the 3rd last token
|
||||
while (count < pIndex && tokens.hasNext()) {
|
||||
blocks = tokens.nextToken();
|
||||
count++;
|
||||
}
|
||||
|
||||
if (blocks != null) {
|
||||
try {
|
||||
return Long.parseLong(blocks) * 1024L;
|
||||
}
|
||||
catch (NumberFormatException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0l;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return "Unix";
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.util.StringTokenIterator;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* UnixFileSystem
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
final class UnixFileSystem extends FileSystem {
|
||||
long getFreeSpace(File pPath) {
|
||||
try {
|
||||
return getNumber(pPath, 3);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
long getTotalSpace(File pPath) {
|
||||
try {
|
||||
return getNumber(pPath, 5);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
private long getNumber(File pPath, int pIndex) throws IOException {
|
||||
// TODO: Test on other platforms
|
||||
// Tested on Mac OS X, CygWin
|
||||
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
|
||||
|
||||
String last = null;
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
last = line;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FileUtil.close(reader);
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
String blocks = null;
|
||||
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
|
||||
int count = 0;
|
||||
// We want the 3rd last token
|
||||
while (count < pIndex && tokens.hasNext()) {
|
||||
blocks = tokens.nextToken();
|
||||
count++;
|
||||
}
|
||||
|
||||
if (blocks != null) {
|
||||
try {
|
||||
return Long.parseLong(blocks) * 1024L;
|
||||
}
|
||||
catch (NumberFormatException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0l;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return "Unix";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,195 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Win32File
|
||||
* <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/Win32File.java#2 $
|
||||
*/
|
||||
final class Win32File extends File {
|
||||
private final static boolean IS_WINDOWS = isWindows();
|
||||
|
||||
private static boolean isWindows() {
|
||||
try {
|
||||
String os = System.getProperty("os.name");
|
||||
return os.toLowerCase().indexOf("windows") >= 0;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Win32File(File pPath) {
|
||||
super(pPath.getPath());
|
||||
}
|
||||
|
||||
public static void main(String[] pArgs) {
|
||||
int argIdx = 0;
|
||||
boolean recursive = false;
|
||||
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
|
||||
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
|
||||
recursive = true;
|
||||
}
|
||||
else {
|
||||
System.err.println("Unknown option: " + pArgs[argIdx]);
|
||||
}
|
||||
argIdx++;
|
||||
}
|
||||
|
||||
File file = wrap(new File(pArgs[argIdx]));
|
||||
System.out.println("file: " + file);
|
||||
System.out.println("file.getClass(): " + file.getClass());
|
||||
|
||||
listFiles(file, 0, recursive);
|
||||
}
|
||||
|
||||
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
|
||||
if (pFile.isDirectory()) {
|
||||
File[] files = pFile.listFiles();
|
||||
for (int l = 0; l < pLevel; l++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.println("Contents of " + pFile + ": ");
|
||||
for (File file : files) {
|
||||
for (int l = 0; l < pLevel; l++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.println(" " + file);
|
||||
if (pRecursive) {
|
||||
listFiles(file, pLevel + 1, pLevel < 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@code File} object pointing to a Windows symbolic link
|
||||
* ({@code .lnk} file) in a {@code Win32Lnk}.
|
||||
* If the operating system is not Windows, the
|
||||
* {@code pPath} parameter is returned unwrapped.
|
||||
*
|
||||
* @param pPath any path, possibly pointing to a Windows symbolic link file.
|
||||
* May be {@code null}, in which case {@code null} is returned.
|
||||
*
|
||||
* @return a new {@code Win32Lnk} object if the current os is Windows, and
|
||||
* the file is a Windows symbolic link ({@code .lnk} file), otherwise
|
||||
* {@code pPath}
|
||||
*/
|
||||
public static File wrap(final File pPath) {
|
||||
if (pPath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IS_WINDOWS) {
|
||||
// Don't wrap if allready wrapped
|
||||
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
|
||||
return pPath;
|
||||
}
|
||||
|
||||
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
|
||||
// If Win32 .lnk, let's wrap
|
||||
try {
|
||||
return new Win32Lnk(pPath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO: FixMe!
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Wwrap even if not a .lnk, as the listFiles() methods etc,
|
||||
// could potentially return .lnk's, that we want to wrap later...
|
||||
return new Win32File(pPath);
|
||||
}
|
||||
|
||||
return pPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@code File} array, possibly pointing to Windows symbolic links
|
||||
* ({@code .lnk} files) in {@code Win32Lnk}s.
|
||||
*
|
||||
* @param pPaths an array of {@code File}s, possibly pointing to Windows
|
||||
* symbolic link files.
|
||||
* May be {@code null}, in which case {@code null} is returned.
|
||||
*
|
||||
* @return {@code pPaths}, with any {@code File} representing a Windows
|
||||
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
|
||||
*/
|
||||
public static File[] wrap(File[] pPaths) {
|
||||
if (IS_WINDOWS) {
|
||||
for (int i = 0; pPaths != null && i < pPaths.length; i++) {
|
||||
pPaths[i] = wrap(pPaths[i]);
|
||||
}
|
||||
}
|
||||
return pPaths;
|
||||
}
|
||||
|
||||
// File overrides
|
||||
@Override
|
||||
public File getAbsoluteFile() {
|
||||
return wrap(super.getAbsoluteFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getCanonicalFile() throws IOException {
|
||||
return wrap(super.getCanonicalFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getParentFile() {
|
||||
return wrap(super.getParentFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] listFiles() {
|
||||
return wrap(super.listFiles());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] listFiles(FileFilter filter) {
|
||||
return wrap(super.listFiles(filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] listFiles(FilenameFilter filter) {
|
||||
return wrap(super.listFiles(filter));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Win32File
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
final class Win32File extends File {
|
||||
private final static boolean IS_WINDOWS = isWindows();
|
||||
|
||||
private static boolean isWindows() {
|
||||
try {
|
||||
String os = System.getProperty("os.name");
|
||||
return os.toLowerCase().indexOf("windows") >= 0;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Win32File(File pPath) {
|
||||
super(pPath.getPath());
|
||||
}
|
||||
|
||||
public static void main(String[] pArgs) {
|
||||
int argIdx = 0;
|
||||
boolean recursive = false;
|
||||
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
|
||||
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
|
||||
recursive = true;
|
||||
}
|
||||
else {
|
||||
System.err.println("Unknown option: " + pArgs[argIdx]);
|
||||
}
|
||||
argIdx++;
|
||||
}
|
||||
|
||||
File file = wrap(new File(pArgs[argIdx]));
|
||||
System.out.println("file: " + file);
|
||||
System.out.println("file.getClass(): " + file.getClass());
|
||||
|
||||
listFiles(file, 0, recursive);
|
||||
}
|
||||
|
||||
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
|
||||
if (pFile.isDirectory()) {
|
||||
File[] files = pFile.listFiles();
|
||||
for (int l = 0; l < pLevel; l++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.println("Contents of " + pFile + ": ");
|
||||
for (File file : files) {
|
||||
for (int l = 0; l < pLevel; l++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.println(" " + file);
|
||||
if (pRecursive) {
|
||||
listFiles(file, pLevel + 1, pLevel < 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@code File} object pointing to a Windows symbolic link
|
||||
* ({@code .lnk} file) in a {@code Win32Lnk}.
|
||||
* If the operating system is not Windows, the
|
||||
* {@code pPath} parameter is returned unwrapped.
|
||||
*
|
||||
* @param pPath any path, possibly pointing to a Windows symbolic link file.
|
||||
* May be {@code null}, in which case {@code null} is returned.
|
||||
*
|
||||
* @return a new {@code Win32Lnk} object if the current os is Windows, and
|
||||
* the file is a Windows symbolic link ({@code .lnk} file), otherwise
|
||||
* {@code pPath}
|
||||
*/
|
||||
public static File wrap(final File pPath) {
|
||||
if (pPath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IS_WINDOWS) {
|
||||
// Don't wrap if allready wrapped
|
||||
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
|
||||
return pPath;
|
||||
}
|
||||
|
||||
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
|
||||
// If Win32 .lnk, let's wrap
|
||||
try {
|
||||
return new Win32Lnk(pPath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO: FixMe!
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Wwrap even if not a .lnk, as the listFiles() methods etc,
|
||||
// could potentially return .lnk's, that we want to wrap later...
|
||||
return new Win32File(pPath);
|
||||
}
|
||||
|
||||
return pPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@code File} array, possibly pointing to Windows symbolic links
|
||||
* ({@code .lnk} files) in {@code Win32Lnk}s.
|
||||
*
|
||||
* @param pPaths an array of {@code File}s, possibly pointing to Windows
|
||||
* symbolic link files.
|
||||
* May be {@code null}, in which case {@code null} is returned.
|
||||
*
|
||||
* @return {@code pPaths}, with any {@code File} representing a Windows
|
||||
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
|
||||
*/
|
||||
public static File[] wrap(File[] pPaths) {
|
||||
if (IS_WINDOWS) {
|
||||
for (int i = 0; pPaths != null && i < pPaths.length; i++) {
|
||||
pPaths[i] = wrap(pPaths[i]);
|
||||
}
|
||||
}
|
||||
return pPaths;
|
||||
}
|
||||
|
||||
// File overrides
|
||||
@Override
|
||||
public File getAbsoluteFile() {
|
||||
return wrap(super.getAbsoluteFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getCanonicalFile() throws IOException {
|
||||
return wrap(super.getCanonicalFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getParentFile() {
|
||||
return wrap(super.getParentFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] listFiles() {
|
||||
return wrap(super.listFiles());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] listFiles(FileFilter filter) {
|
||||
return wrap(super.listFiles(filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] listFiles(FilenameFilter filter) {
|
||||
return wrap(super.listFiles(filter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,92 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* WindowsFileSystem
|
||||
* <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/Win32FileSystem.java#2 $
|
||||
*/
|
||||
final class Win32FileSystem extends FileSystem {
|
||||
public long getFreeSpace(File pPath) {
|
||||
try {
|
||||
// Windows version
|
||||
// TODO: Test on W2K/95/98/etc... (tested on XP)
|
||||
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
|
||||
|
||||
String last = null;
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
last = line;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FileUtil.close(reader);
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
int end = last.lastIndexOf(" bytes free");
|
||||
int start = last.lastIndexOf(' ', end - 1);
|
||||
|
||||
if (start >= 0 && end >= 0) {
|
||||
try {
|
||||
return Long.parseLong(last.substring(start + 1, end));
|
||||
}
|
||||
catch (NumberFormatException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return 0l;
|
||||
}
|
||||
|
||||
long getTotalSpace(File pPath) {
|
||||
// TODO: Implement, probably need some JNI stuff...
|
||||
// Distribute df.exe and execute from temp!? ;-)
|
||||
return getFreeSpace(pPath);
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return "Win32";
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* WindowsFileSystem
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
final class Win32FileSystem extends FileSystem {
|
||||
public long getFreeSpace(File pPath) {
|
||||
try {
|
||||
// Windows version
|
||||
// TODO: Test on W2K/95/98/etc... (tested on XP)
|
||||
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
|
||||
|
||||
String last = null;
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
last = line;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FileUtil.close(reader);
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
int end = last.lastIndexOf(" bytes free");
|
||||
int start = last.lastIndexOf(' ', end - 1);
|
||||
|
||||
if (start >= 0 && end >= 0) {
|
||||
try {
|
||||
return Long.parseLong(last.substring(start + 1, end));
|
||||
}
|
||||
catch (NumberFormatException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return 0l;
|
||||
}
|
||||
|
||||
long getTotalSpace(File pPath) {
|
||||
// TODO: Implement, probably need some JNI stuff...
|
||||
// Distribute df.exe and execute from temp!? ;-)
|
||||
return getFreeSpace(pPath);
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return "Win32";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,475 +1,477 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
|
||||
* <p/>
|
||||
* This class is based on example code from
|
||||
* <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.
|
||||
*
|
||||
* @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 = {
|
||||
'L', 0x00, 0x00, 0x00, // Magic
|
||||
};
|
||||
private final static byte[] LNK_GUID = {
|
||||
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 static final int FLAG_ITEM_ID_LIST = 0x01;
|
||||
private static final int FLAG_FILE_LOC_INFO = 0x02;
|
||||
private static final int FLAG_DESC_STRING = 0x04;
|
||||
private static final int FLAG_REL_PATH_STRING = 0x08;
|
||||
private static final int FLAG_WORKING_DIRECTORY = 0x10;
|
||||
private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
|
||||
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);
|
||||
File target = parse(this);
|
||||
if (target == this) {
|
||||
// NOTE: This is a workaround
|
||||
// target = this causes infinite loops in some methods
|
||||
target = new File(pPath);
|
||||
}
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
Win32Lnk(final File pPath) throws IOException {
|
||||
this(pPath.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws java.io.IOException if the {@code .lnk} cannot be parsed
|
||||
*/
|
||||
static File parse(final File pPath) throws IOException {
|
||||
if (!pPath.getName().endsWith(".lnk")) {
|
||||
return pPath;
|
||||
}
|
||||
|
||||
File result = pPath;
|
||||
|
||||
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
|
||||
try {
|
||||
byte[] magic = new byte[4];
|
||||
in.readFully(magic);
|
||||
|
||||
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");
|
||||
// Not a symlink
|
||||
return pPath;
|
||||
}
|
||||
|
||||
// Get the flags
|
||||
int flags = in.readInt();
|
||||
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
|
||||
|
||||
// Get to the file settings
|
||||
/*int attributes = */in.readInt();
|
||||
|
||||
// File attributes
|
||||
// 0 Target is read only.
|
||||
// 1 Target is hidden.
|
||||
// 2 Target is a system file.
|
||||
// 3 Target is a volume label. (Not possible)
|
||||
// 4 Target is a directory.
|
||||
// 5 Target has been modified since last backup. (archive)
|
||||
// 6 Target is encrypted (NTFS EFS)
|
||||
// 7 Target is Normal??
|
||||
// 8 Target is temporary.
|
||||
// 9 Target is a sparse file.
|
||||
// 10 Target has reparse point data.
|
||||
// 11 Target is compressed.
|
||||
// 12 Target is offline.
|
||||
//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...
|
||||
|
||||
// Skipped data:
|
||||
// long time 1 (creation)
|
||||
// long time 2 (modification)
|
||||
// long time 3 (last access)
|
||||
// int file length
|
||||
// int icon number
|
||||
// int ShowVnd value
|
||||
// int hotkey
|
||||
// int, int - unknown: 0,0
|
||||
|
||||
// If the shell settings are present, skip them
|
||||
if ((flags & FLAG_ITEM_ID_LIST) != 0) {
|
||||
// Shell Item Id List present
|
||||
//System.out.println("Shell Item Id List present");
|
||||
int shellLen = in.readShort(); // Short
|
||||
//System.out.println("shellLen: " + shellLen);
|
||||
|
||||
// TODO: Probably need to parse this data, to determine
|
||||
// Cygwin folders...
|
||||
|
||||
/*
|
||||
int read = 2;
|
||||
int itemLen = in.readShort();
|
||||
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
|
||||
//in.readFully(itemBytes);
|
||||
|
||||
String item = reader.readLine();
|
||||
System.out.println("item: \"" + item + "\"");
|
||||
|
||||
itemLen = in.readShort();
|
||||
read += itemLen;
|
||||
}
|
||||
|
||||
System.out.println("read: " + read);
|
||||
*/
|
||||
|
||||
in.skipBytes(shellLen);
|
||||
}
|
||||
|
||||
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
|
||||
// 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
|
||||
// 8h 1 dword Flags
|
||||
// Ch 1 dword Offset of local volume info
|
||||
// 10h 1 dword Offset of base pathname on local system
|
||||
// 14h 1 dword Offset of network volume info
|
||||
// 18h 1 dword Offset of remaining pathname
|
||||
|
||||
// Flags:
|
||||
// Bit Meaning
|
||||
// 0 Available on a local volume
|
||||
// 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);
|
||||
|
||||
in.readInt(); // Skip
|
||||
|
||||
int locFlags = in.readInt();
|
||||
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
|
||||
if ((locFlags & 0x01) != 0) {
|
||||
//System.out.println("Available local");
|
||||
}
|
||||
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
|
||||
|
||||
int localSysOff = in.readInt();
|
||||
//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);
|
||||
String path = new String(pathBytes, 0, pathBytes.length - 1);
|
||||
/*
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
byte read;
|
||||
// Read bytes until the null (0) character
|
||||
while (true) {
|
||||
read = in.readByte();
|
||||
if (read == 0) {
|
||||
break;
|
||||
}
|
||||
bytes.write(read & 0xff);
|
||||
}
|
||||
|
||||
String path = new String(bytes.toByteArray(), 0, bytes.size());
|
||||
//*/
|
||||
|
||||
// Recurse to end of link chain
|
||||
// TODO: This may cause endless loop if cyclic chain...
|
||||
//System.out.println("path: \"" + path + "\"");
|
||||
try {
|
||||
result = parse(new File(path));
|
||||
}
|
||||
catch (StackOverflowError e) {
|
||||
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & FLAG_DESC_STRING) != 0) {
|
||||
// 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();
|
||||
//System.out.println("descLen: " + descLen);
|
||||
|
||||
byte[] descBytes = new byte[descLen];
|
||||
in.readFully(descBytes, 0, descLen);
|
||||
|
||||
//String desc = new String(descBytes, 0, descLen);
|
||||
//System.out.println("desc: " + desc);
|
||||
}
|
||||
|
||||
if ((flags & FLAG_REL_PATH_STRING) != 0) {
|
||||
// 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();
|
||||
//System.out.println("pathLen: " + pathLen);
|
||||
|
||||
byte[] pathBytes = new byte[pathLen];
|
||||
in.readFully(pathBytes, 0, pathLen);
|
||||
|
||||
String path = new String(pathBytes, 0, pathLen);
|
||||
|
||||
// TODO: This may cause endless loop if cyclic chain...
|
||||
//System.out.println("path: \"" + path + "\"");
|
||||
if (result == pPath) {
|
||||
try {
|
||||
result = parse(new File(pPath.getParentFile(), path));
|
||||
}
|
||||
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_COMMAND_LINE_ARGS) != 0) {
|
||||
//System.out.println("Command Line Arguments present");
|
||||
// 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_ADDITIONAL_INFO) != 0) {
|
||||
//System.out.println("Additional Info present");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
private static String getNullDelimitedString(byte[] bytes, int off) {
|
||||
int len = 0;
|
||||
// Count bytes until the null (0) character
|
||||
while (true) {
|
||||
if (bytes[off + len] == 0) {
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
System.err.println("--> " + len);
|
||||
|
||||
return new String(bytes, off, len);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts two bytes into a short.
|
||||
* <p/>
|
||||
* NOTE: this is little endian because it's for an
|
||||
* Intel only OS
|
||||
*
|
||||
* @ 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);
|
||||
}
|
||||
*/
|
||||
|
||||
public File getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
// java.io.File overrides below
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return target.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return target.canRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite() {
|
||||
return target.canWrite();
|
||||
}
|
||||
|
||||
// NOTE: equals is implemented using compareto == 0
|
||||
/*
|
||||
public int compareTo(File pathname) {
|
||||
// TODO: Verify this
|
||||
// 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
|
||||
|
||||
// Deletes only the .lnk
|
||||
// public boolean delete() {
|
||||
//public void deleteOnExit() {
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return target.exists();
|
||||
}
|
||||
|
||||
// A .lnk may be absolute
|
||||
//public File getAbsoluteFile() {
|
||||
//public String getAbsolutePath() {
|
||||
|
||||
// Theses should be resolved according to the API (for Unix).
|
||||
@Override
|
||||
public File getCanonicalFile() throws IOException {
|
||||
return target.getCanonicalFile();
|
||||
}
|
||||
|
||||
@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 getParent() {
|
||||
//public File getParentFile() {
|
||||
|
||||
// public boolean isAbsolute() {
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return target.isFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return target.isHidden();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return target.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() {
|
||||
return target.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list() {
|
||||
return target.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list(final FilenameFilter filter) {
|
||||
return target.list(filter);
|
||||
}
|
||||
|
||||
@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 FilenameFilter filter) {
|
||||
return Win32File.wrap(target.listFiles(filter));
|
||||
}
|
||||
|
||||
// Makes no sense, does it?
|
||||
//public boolean mkdir() {
|
||||
//public boolean mkdirs() {
|
||||
|
||||
// Only rename the lnk
|
||||
//public boolean renameTo(File dest) {
|
||||
|
||||
@Override
|
||||
public boolean setLastModified(long time) {
|
||||
return target.setLastModified(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setReadOnly() {
|
||||
return target.setReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (target.equals(this)) {
|
||||
return super.toString();
|
||||
}
|
||||
return super.toString() + " -> " + target.toString();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
|
||||
* <p>
|
||||
* This class is based on example code from
|
||||
* <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.
|
||||
* </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 $
|
||||
*/
|
||||
final class Win32Lnk extends File {
|
||||
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
|
||||
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
|
||||
};
|
||||
|
||||
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_DESC_STRING = 0x04;
|
||||
private static final int FLAG_REL_PATH_STRING = 0x08;
|
||||
private static final int FLAG_WORKING_DIRECTORY = 0x10;
|
||||
private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
|
||||
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);
|
||||
File target = parse(this);
|
||||
if (target == this) {
|
||||
// NOTE: This is a workaround
|
||||
// target = this causes infinite loops in some methods
|
||||
target = new File(pPath);
|
||||
}
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
Win32Lnk(final File pPath) throws IOException {
|
||||
this(pPath.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws java.io.IOException if the {@code .lnk} cannot be parsed
|
||||
*/
|
||||
static File parse(final File pPath) throws IOException {
|
||||
if (!pPath.getName().endsWith(".lnk")) {
|
||||
return pPath;
|
||||
}
|
||||
|
||||
File result = pPath;
|
||||
|
||||
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
|
||||
try {
|
||||
byte[] magic = new byte[4];
|
||||
in.readFully(magic);
|
||||
|
||||
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");
|
||||
// Not a symlink
|
||||
return pPath;
|
||||
}
|
||||
|
||||
// Get the flags
|
||||
int flags = in.readInt();
|
||||
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
|
||||
|
||||
// Get to the file settings
|
||||
/*int attributes = */in.readInt();
|
||||
|
||||
// File attributes
|
||||
// 0 Target is read only.
|
||||
// 1 Target is hidden.
|
||||
// 2 Target is a system file.
|
||||
// 3 Target is a volume label. (Not possible)
|
||||
// 4 Target is a directory.
|
||||
// 5 Target has been modified since last backup. (archive)
|
||||
// 6 Target is encrypted (NTFS EFS)
|
||||
// 7 Target is Normal??
|
||||
// 8 Target is temporary.
|
||||
// 9 Target is a sparse file.
|
||||
// 10 Target has reparse point data.
|
||||
// 11 Target is compressed.
|
||||
// 12 Target is offline.
|
||||
//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...
|
||||
|
||||
// Skipped data:
|
||||
// long time 1 (creation)
|
||||
// long time 2 (modification)
|
||||
// long time 3 (last access)
|
||||
// int file length
|
||||
// int icon number
|
||||
// int ShowVnd value
|
||||
// int hotkey
|
||||
// int, int - unknown: 0,0
|
||||
|
||||
// If the shell settings are present, skip them
|
||||
if ((flags & FLAG_ITEM_ID_LIST) != 0) {
|
||||
// Shell Item Id List present
|
||||
//System.out.println("Shell Item Id List present");
|
||||
int shellLen = in.readShort(); // Short
|
||||
//System.out.println("shellLen: " + shellLen);
|
||||
|
||||
// TODO: Probably need to parse this data, to determine
|
||||
// Cygwin folders...
|
||||
|
||||
/*
|
||||
int read = 2;
|
||||
int itemLen = in.readShort();
|
||||
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
|
||||
//in.readFully(itemBytes);
|
||||
|
||||
String item = reader.readLine();
|
||||
System.out.println("item: \"" + item + "\"");
|
||||
|
||||
itemLen = in.readShort();
|
||||
read += itemLen;
|
||||
}
|
||||
|
||||
System.out.println("read: " + read);
|
||||
*/
|
||||
|
||||
in.skipBytes(shellLen);
|
||||
}
|
||||
|
||||
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
|
||||
// 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
|
||||
// 8h 1 dword Flags
|
||||
// Ch 1 dword Offset of local volume info
|
||||
// 10h 1 dword Offset of base pathname on local system
|
||||
// 14h 1 dword Offset of network volume info
|
||||
// 18h 1 dword Offset of remaining pathname
|
||||
|
||||
// Flags:
|
||||
// Bit Meaning
|
||||
// 0 Available on a local volume
|
||||
// 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);
|
||||
|
||||
in.readInt(); // Skip
|
||||
|
||||
int locFlags = in.readInt();
|
||||
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
|
||||
if ((locFlags & 0x01) != 0) {
|
||||
//System.out.println("Available local");
|
||||
}
|
||||
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
|
||||
|
||||
int localSysOff = in.readInt();
|
||||
//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);
|
||||
String path = new String(pathBytes, 0, pathBytes.length - 1);
|
||||
/*
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
byte read;
|
||||
// Read bytes until the null (0) character
|
||||
while (true) {
|
||||
read = in.readByte();
|
||||
if (read == 0) {
|
||||
break;
|
||||
}
|
||||
bytes.write(read & 0xff);
|
||||
}
|
||||
|
||||
String path = new String(bytes.toByteArray(), 0, bytes.size());
|
||||
//*/
|
||||
|
||||
// Recurse to end of link chain
|
||||
// TODO: This may cause endless loop if cyclic chain...
|
||||
//System.out.println("path: \"" + path + "\"");
|
||||
try {
|
||||
result = parse(new File(path));
|
||||
}
|
||||
catch (StackOverflowError e) {
|
||||
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & FLAG_DESC_STRING) != 0) {
|
||||
// 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();
|
||||
//System.out.println("descLen: " + descLen);
|
||||
|
||||
byte[] descBytes = new byte[descLen];
|
||||
in.readFully(descBytes, 0, descLen);
|
||||
|
||||
//String desc = new String(descBytes, 0, descLen);
|
||||
//System.out.println("desc: " + desc);
|
||||
}
|
||||
|
||||
if ((flags & FLAG_REL_PATH_STRING) != 0) {
|
||||
// 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();
|
||||
//System.out.println("pathLen: " + pathLen);
|
||||
|
||||
byte[] pathBytes = new byte[pathLen];
|
||||
in.readFully(pathBytes, 0, pathLen);
|
||||
|
||||
String path = new String(pathBytes, 0, pathLen);
|
||||
|
||||
// TODO: This may cause endless loop if cyclic chain...
|
||||
//System.out.println("path: \"" + path + "\"");
|
||||
if (result == pPath) {
|
||||
try {
|
||||
result = parse(new File(pPath.getParentFile(), path));
|
||||
}
|
||||
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_COMMAND_LINE_ARGS) != 0) {
|
||||
//System.out.println("Command Line Arguments present");
|
||||
// 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_ADDITIONAL_INFO) != 0) {
|
||||
//System.out.println("Additional Info present");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
private static String getNullDelimitedString(byte[] bytes, int off) {
|
||||
int len = 0;
|
||||
// Count bytes until the null (0) character
|
||||
while (true) {
|
||||
if (bytes[off + len] == 0) {
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
System.err.println("--> " + len);
|
||||
|
||||
return new String(bytes, off, len);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts two bytes into a short.
|
||||
* <p>
|
||||
* NOTE: this is little endian because it's for an
|
||||
* Intel only OS
|
||||
* </p>
|
||||
*
|
||||
* @ 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);
|
||||
}
|
||||
*/
|
||||
|
||||
public File getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
// java.io.File overrides below
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return target.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return target.canRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite() {
|
||||
return target.canWrite();
|
||||
}
|
||||
|
||||
// NOTE: equals is implemented using compareto == 0
|
||||
/*
|
||||
public int compareTo(File pathname) {
|
||||
// TODO: Verify this
|
||||
// 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
|
||||
|
||||
// Deletes only the .lnk
|
||||
// public boolean delete() {
|
||||
//public void deleteOnExit() {
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return target.exists();
|
||||
}
|
||||
|
||||
// A .lnk may be absolute
|
||||
//public File getAbsoluteFile() {
|
||||
//public String getAbsolutePath() {
|
||||
|
||||
// Theses should be resolved according to the API (for Unix).
|
||||
@Override
|
||||
public File getCanonicalFile() throws IOException {
|
||||
return target.getCanonicalFile();
|
||||
}
|
||||
|
||||
@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 getParent() {
|
||||
//public File getParentFile() {
|
||||
|
||||
// public boolean isAbsolute() {
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return target.isFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return target.isHidden();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return target.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() {
|
||||
return target.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list() {
|
||||
return target.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list(final FilenameFilter filter) {
|
||||
return target.list(filter);
|
||||
}
|
||||
|
||||
@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 FilenameFilter filter) {
|
||||
return Win32File.wrap(target.listFiles(filter));
|
||||
}
|
||||
|
||||
// Makes no sense, does it?
|
||||
//public boolean mkdir() {
|
||||
//public boolean mkdirs() {
|
||||
|
||||
// Only rename the lnk
|
||||
//public boolean renameTo(File dest) {
|
||||
|
||||
@Override
|
||||
public boolean setLastModified(long time) {
|
||||
return target.setLastModified(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setReadOnly() {
|
||||
return target.setReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (target.equals(this)) {
|
||||
return super.toString();
|
||||
}
|
||||
return super.toString() + " -> " + target.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,239 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.DateUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Wraps a {@code Writer} in an {@code OutputStream}.
|
||||
* <p/>
|
||||
* <em>Instances of this class are not thread-safe.</em>
|
||||
* <p/>
|
||||
* <em>NOTE: This class is probably not the right way of solving your problem,
|
||||
* however it might prove useful in JSPs etc.
|
||||
* If possible, it's always better to use the {@code Writer}'s underlying
|
||||
* {@code OutputStream}, or wrap it's native backing.
|
||||
* </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 $
|
||||
*/
|
||||
public class WriterOutputStream extends OutputStream {
|
||||
protected Writer writer;
|
||||
final protected Decoder decoder;
|
||||
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
|
||||
|
||||
private volatile boolean isFlushing = false; // Ugly but critical...
|
||||
|
||||
private static final boolean NIO_AVAILABLE = isNIOAvailable();
|
||||
|
||||
private static boolean isNIOAvailable() {
|
||||
try {
|
||||
Class.forName("java.nio.charset.Charset");
|
||||
return true;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public WriterOutputStream(final Writer pWriter, final String pCharset) {
|
||||
writer = pWriter;
|
||||
decoder = getDecoder(pCharset);
|
||||
}
|
||||
|
||||
public WriterOutputStream(final Writer pWriter) {
|
||||
this(pWriter, null);
|
||||
}
|
||||
|
||||
private static Decoder getDecoder(final String pCharset) {
|
||||
// NOTE: The CharsetDecoder is typically 10-20% faster than
|
||||
// StringDecoder according to my tests
|
||||
// StringEncoder is horribly slow on 1.2 systems, but there's no
|
||||
// alternative...
|
||||
if (NIO_AVAILABLE) {
|
||||
return new CharsetDecoder(pCharset);
|
||||
}
|
||||
|
||||
return new StringDecoder(pCharset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
writer.close();
|
||||
writer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
flushBuffer();
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(byte[] pBytes) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException("bytes == null");
|
||||
}
|
||||
write(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||
flushBuffer();
|
||||
decoder.decodeTo(writer, pBytes, pOffset, pLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(int pByte) {
|
||||
// TODO: Is it possible to know if this is a good place in the stream to
|
||||
// flush? It might be in the middle of a multi-byte encoded character..
|
||||
bufferStream.write(pByte);
|
||||
}
|
||||
|
||||
private void flushBuffer() throws IOException {
|
||||
if (!isFlushing && bufferStream.size() > 0) {
|
||||
isFlushing = true;
|
||||
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
|
||||
bufferStream.reset();
|
||||
isFlushing = false;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
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");
|
||||
|
||||
Decoder d;
|
||||
long start;
|
||||
long time;
|
||||
Writer sink = new PrintWriter(new NullOutputStream());
|
||||
StringWriter writer;
|
||||
String str;
|
||||
|
||||
d = new StringDecoder("UTF-8");
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
time = DateUtil.delta(start);
|
||||
System.out.println("StringDecoder");
|
||||
System.out.println("time: " + time);
|
||||
|
||||
writer = new StringWriter();
|
||||
d.decodeTo(writer, bytes, 0, bytes.length);
|
||||
str = writer.toString();
|
||||
System.out.println("str: \"" + str + "\"");
|
||||
System.out.println("chars.length: " + str.length());
|
||||
System.out.println();
|
||||
|
||||
if (NIO_AVAILABLE) {
|
||||
d = new CharsetDecoder("UTF-8");
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
time = DateUtil.delta(start);
|
||||
System.out.println("CharsetDecoder");
|
||||
System.out.println("time: " + time);
|
||||
writer = new StringWriter();
|
||||
d.decodeTo(writer, bytes, 0, bytes.length);
|
||||
str = writer.toString();
|
||||
System.out.println("str: \"" + str + "\"");
|
||||
System.out.println("chars.length: " + str.length());
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
|
||||
os.write(bytes);
|
||||
os.flush();
|
||||
System.out.println();
|
||||
|
||||
for (byte b : bytes) {
|
||||
os.write(b & 0xff);
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
|
||||
CharsetDecoder(String pCharset) {
|
||||
// Handle null-case, to get default charset
|
||||
String charset = pCharset != null ? pCharset :
|
||||
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));
|
||||
pWriter.write(cb.array(), 0, cb.length());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StringDecoder implements Decoder {
|
||||
final String mCharset;
|
||||
|
||||
StringDecoder(String pCharset) {
|
||||
mCharset = pCharset;
|
||||
}
|
||||
|
||||
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||
String str = mCharset == null ?
|
||||
new String(pBytes, pOffset, pLength) :
|
||||
new String(pBytes, pOffset, pLength, mCharset);
|
||||
|
||||
pWriter.write(str);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io;
|
||||
|
||||
import com.twelvemonkeys.lang.DateUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Wraps a {@code Writer} in an {@code OutputStream}.
|
||||
* <p>
|
||||
* <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,
|
||||
* however it might prove useful in JSPs etc.
|
||||
* If possible, it's always better to use the {@code Writer}'s underlying
|
||||
* {@code OutputStream}, or wrap it's native backing.
|
||||
* </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 $
|
||||
*/
|
||||
public class WriterOutputStream extends OutputStream {
|
||||
protected Writer writer;
|
||||
final protected Decoder decoder;
|
||||
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
|
||||
|
||||
private volatile boolean isFlushing = false; // Ugly but critical...
|
||||
|
||||
private static final boolean NIO_AVAILABLE = isNIOAvailable();
|
||||
|
||||
private static boolean isNIOAvailable() {
|
||||
try {
|
||||
Class.forName("java.nio.charset.Charset");
|
||||
return true;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public WriterOutputStream(final Writer pWriter, final String pCharset) {
|
||||
writer = pWriter;
|
||||
decoder = getDecoder(pCharset);
|
||||
}
|
||||
|
||||
public WriterOutputStream(final Writer pWriter) {
|
||||
this(pWriter, null);
|
||||
}
|
||||
|
||||
private static Decoder getDecoder(final String pCharset) {
|
||||
// NOTE: The CharsetDecoder is typically 10-20% faster than
|
||||
// StringDecoder according to my tests
|
||||
// StringEncoder is horribly slow on 1.2 systems, but there's no
|
||||
// alternative...
|
||||
if (NIO_AVAILABLE) {
|
||||
return new CharsetDecoder(pCharset);
|
||||
}
|
||||
|
||||
return new StringDecoder(pCharset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
writer.close();
|
||||
writer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
flushBuffer();
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(byte[] pBytes) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException("bytes == null");
|
||||
}
|
||||
write(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||
flushBuffer();
|
||||
decoder.decodeTo(writer, pBytes, pOffset, pLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(int pByte) {
|
||||
// TODO: Is it possible to know if this is a good place in the stream to
|
||||
// flush? It might be in the middle of a multi-byte encoded character..
|
||||
bufferStream.write(pByte);
|
||||
}
|
||||
|
||||
private void flushBuffer() throws IOException {
|
||||
if (!isFlushing && bufferStream.size() > 0) {
|
||||
isFlushing = true;
|
||||
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
|
||||
bufferStream.reset();
|
||||
isFlushing = false;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
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");
|
||||
|
||||
Decoder d;
|
||||
long start;
|
||||
long time;
|
||||
Writer sink = new PrintWriter(new NullOutputStream());
|
||||
StringWriter writer;
|
||||
String str;
|
||||
|
||||
d = new StringDecoder("UTF-8");
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
time = DateUtil.delta(start);
|
||||
System.out.println("StringDecoder");
|
||||
System.out.println("time: " + time);
|
||||
|
||||
writer = new StringWriter();
|
||||
d.decodeTo(writer, bytes, 0, bytes.length);
|
||||
str = writer.toString();
|
||||
System.out.println("str: \"" + str + "\"");
|
||||
System.out.println("chars.length: " + str.length());
|
||||
System.out.println();
|
||||
|
||||
if (NIO_AVAILABLE) {
|
||||
d = new CharsetDecoder("UTF-8");
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||
}
|
||||
time = DateUtil.delta(start);
|
||||
System.out.println("CharsetDecoder");
|
||||
System.out.println("time: " + time);
|
||||
writer = new StringWriter();
|
||||
d.decodeTo(writer, bytes, 0, bytes.length);
|
||||
str = writer.toString();
|
||||
System.out.println("str: \"" + str + "\"");
|
||||
System.out.println("chars.length: " + str.length());
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
|
||||
os.write(bytes);
|
||||
os.flush();
|
||||
System.out.println();
|
||||
|
||||
for (byte b : bytes) {
|
||||
os.write(b & 0xff);
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
|
||||
CharsetDecoder(String pCharset) {
|
||||
// Handle null-case, to get default charset
|
||||
String charset = pCharset != null ? pCharset :
|
||||
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));
|
||||
pWriter.write(cb.array(), 0, cb.length());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StringDecoder implements Decoder {
|
||||
final String mCharset;
|
||||
|
||||
StringDecoder(String pCharset) {
|
||||
mCharset = pCharset;
|
||||
}
|
||||
|
||||
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||
String str = mCharset == null ?
|
||||
new String(pBytes, pOffset, pLength) :
|
||||
new String(pBytes, pOffset, pLength, mCharset);
|
||||
|
||||
pWriter.write(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,188 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* {@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/rfc2045"RFC 2045</a>
|
||||
*
|
||||
* @see Base64Encoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class Base64Decoder implements Decoder {
|
||||
/**
|
||||
* This array maps the characters to their 6 bit values
|
||||
*/
|
||||
final static byte[] PEM_ARRAY = {
|
||||
//0 1 2 3 4 5 6 7
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
|
||||
'4', '5', '6', '7', '8', '9', '+', '/' // 7
|
||||
};
|
||||
|
||||
final static byte[] PEM_CONVERT_ARRAY;
|
||||
|
||||
private byte[] decodeBuffer = new byte[4];
|
||||
|
||||
static {
|
||||
PEM_CONVERT_ARRAY = new byte[256];
|
||||
|
||||
for (int i = 0; i < 255; i++) {
|
||||
PEM_CONVERT_ARRAY[i] = -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < PEM_ARRAY.length; 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)
|
||||
throws IOException
|
||||
{
|
||||
for (int i = 0; i < pLength; i++) {
|
||||
int read = pStream.read();
|
||||
|
||||
if (read == -1) {
|
||||
return i != 0 ? i : -1;
|
||||
}
|
||||
|
||||
pBytes[i + pOffset] = (byte) read;
|
||||
}
|
||||
|
||||
return pLength;
|
||||
}
|
||||
|
||||
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
|
||||
throws IOException {
|
||||
|
||||
byte byte0 = -1;
|
||||
byte byte1 = -1;
|
||||
byte byte2 = -1;
|
||||
byte byte3 = -1;
|
||||
|
||||
if (pLength < 2) {
|
||||
throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
|
||||
}
|
||||
|
||||
int read;
|
||||
|
||||
// Skip line feeds
|
||||
do {
|
||||
read = pInput.read();
|
||||
|
||||
if (read == -1) {
|
||||
return false;
|
||||
}
|
||||
} while (read == 10 || read == 13);
|
||||
|
||||
decodeBuffer[0] = (byte) read;
|
||||
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
|
||||
|
||||
if (read == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = pLength;
|
||||
|
||||
if (length > 3 && decodeBuffer[3] == 61) {
|
||||
length = 3;
|
||||
}
|
||||
|
||||
if (length > 2 && decodeBuffer[2] == 61) {
|
||||
length = 2;
|
||||
}
|
||||
|
||||
switch (length) {
|
||||
case 4:
|
||||
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
|
||||
// fall through
|
||||
case 3:
|
||||
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
|
||||
// fall through
|
||||
case 2:
|
||||
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
|
||||
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
|
||||
// fall through
|
||||
default:
|
||||
switch (length) {
|
||||
case 2:
|
||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||
break;
|
||||
case 3:
|
||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||
break;
|
||||
case 4:
|
||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||
do {
|
||||
int k = 72;
|
||||
int i;
|
||||
|
||||
for (i = 0; i + 4 < k; i += 4) {
|
||||
if(!decodeAtom(stream, buffer, 4)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!decodeAtom(stream, buffer, k - i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
|
||||
|
||||
return buffer.position();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* {@code Decoder} implementation for standard base64 encoding.
|
||||
*
|
||||
* @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 Base64Encoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class Base64Decoder implements Decoder {
|
||||
/**
|
||||
* This array maps the characters to their 6 bit values
|
||||
*/
|
||||
final static byte[] PEM_ARRAY = {
|
||||
//0 1 2 3 4 5 6 7
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
|
||||
'4', '5', '6', '7', '8', '9', '+', '/' // 7
|
||||
};
|
||||
|
||||
final static byte[] PEM_CONVERT_ARRAY;
|
||||
|
||||
private byte[] decodeBuffer = new byte[4];
|
||||
|
||||
static {
|
||||
PEM_CONVERT_ARRAY = new byte[256];
|
||||
|
||||
for (int i = 0; i < 255; i++) {
|
||||
PEM_CONVERT_ARRAY[i] = -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < PEM_ARRAY.length; 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)
|
||||
throws IOException
|
||||
{
|
||||
for (int i = 0; i < pLength; i++) {
|
||||
int read = pStream.read();
|
||||
|
||||
if (read == -1) {
|
||||
return i != 0 ? i : -1;
|
||||
}
|
||||
|
||||
pBytes[i + pOffset] = (byte) read;
|
||||
}
|
||||
|
||||
return pLength;
|
||||
}
|
||||
|
||||
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
|
||||
throws IOException {
|
||||
|
||||
byte byte0 = -1;
|
||||
byte byte1 = -1;
|
||||
byte byte2 = -1;
|
||||
byte byte3 = -1;
|
||||
|
||||
if (pLength < 2) {
|
||||
throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
|
||||
}
|
||||
|
||||
int read;
|
||||
|
||||
// Skip line feeds
|
||||
do {
|
||||
read = pInput.read();
|
||||
|
||||
if (read == -1) {
|
||||
return false;
|
||||
}
|
||||
} while (read == 10 || read == 13);
|
||||
|
||||
decodeBuffer[0] = (byte) read;
|
||||
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
|
||||
|
||||
if (read == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = pLength;
|
||||
|
||||
if (length > 3 && decodeBuffer[3] == 61) {
|
||||
length = 3;
|
||||
}
|
||||
|
||||
if (length > 2 && decodeBuffer[2] == 61) {
|
||||
length = 2;
|
||||
}
|
||||
|
||||
switch (length) {
|
||||
case 4:
|
||||
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
|
||||
// fall through
|
||||
case 3:
|
||||
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
|
||||
// fall through
|
||||
case 2:
|
||||
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
|
||||
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
|
||||
// fall through
|
||||
default:
|
||||
switch (length) {
|
||||
case 2:
|
||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||
break;
|
||||
case 3:
|
||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||
break;
|
||||
case 4:
|
||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||
do {
|
||||
int k = 72;
|
||||
int i;
|
||||
|
||||
for (i = 0; i + 4 < k; i += 4) {
|
||||
if(!decodeAtom(stream, buffer, 4)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!decodeAtom(stream, buffer, k - i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
|
||||
|
||||
return buffer.position();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,106 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* {@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/rfc2045"RFC 2045</a>
|
||||
*
|
||||
* @see Base64Decoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class Base64Encoder implements Encoder {
|
||||
|
||||
public void encode(final OutputStream stream, final ByteBuffer buffer)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// TODO: Implement
|
||||
// NOTE: This is impossible, given the current spec, as we need to either:
|
||||
// - buffer all data in the EncoderStream
|
||||
// - or have flush/end method(s) in the Encoder
|
||||
// to ensure proper end of stream handling
|
||||
|
||||
int length;
|
||||
|
||||
// TODO: Temp impl, will only work for single writes
|
||||
while (buffer.hasRemaining()) {
|
||||
byte a, b, c;
|
||||
|
||||
// if ((buffer.remaining()) > 2) {
|
||||
// length = 3;
|
||||
// }
|
||||
// else {
|
||||
// length = buffer.remaining();
|
||||
// }
|
||||
length = Math.min(3, buffer.remaining());
|
||||
|
||||
switch (length) {
|
||||
case 1:
|
||||
a = buffer.get();
|
||||
b = 0;
|
||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||
stream.write('=');
|
||||
stream.write('=');
|
||||
break;
|
||||
|
||||
case 2:
|
||||
a = buffer.get();
|
||||
b = buffer.get();
|
||||
c = 0;
|
||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
|
||||
stream.write('=');
|
||||
break;
|
||||
|
||||
default:
|
||||
a = buffer.get();
|
||||
b = buffer.get();
|
||||
c = buffer.get();
|
||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||
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[c & 0x3F]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* {@code Encoder} implementation for standard base64 encoding.
|
||||
*
|
||||
* @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 Base64Decoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class Base64Encoder implements Encoder {
|
||||
|
||||
public void encode(final OutputStream stream, final ByteBuffer buffer)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// TODO: Implement
|
||||
// NOTE: This is impossible, given the current spec, as we need to either:
|
||||
// - buffer all data in the EncoderStream
|
||||
// - or have flush/end method(s) in the Encoder
|
||||
// to ensure proper end of stream handling
|
||||
|
||||
int length;
|
||||
|
||||
// TODO: Temp impl, will only work for single writes
|
||||
while (buffer.hasRemaining()) {
|
||||
byte a, b, c;
|
||||
|
||||
// if ((buffer.remaining()) > 2) {
|
||||
// length = 3;
|
||||
// }
|
||||
// else {
|
||||
// length = buffer.remaining();
|
||||
// }
|
||||
length = Math.min(3, buffer.remaining());
|
||||
|
||||
switch (length) {
|
||||
case 1:
|
||||
a = buffer.get();
|
||||
b = 0;
|
||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||
stream.write('=');
|
||||
stream.write('=');
|
||||
break;
|
||||
|
||||
case 2:
|
||||
a = buffer.get();
|
||||
b = buffer.get();
|
||||
c = 0;
|
||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
|
||||
stream.write('=');
|
||||
break;
|
||||
|
||||
default:
|
||||
a = buffer.get();
|
||||
b = buffer.get();
|
||||
c = buffer.get();
|
||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||
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[c & 0x3F]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
||||
* <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/enc/DecodeException.java#2 $
|
||||
*/
|
||||
public class DecodeException extends IOException {
|
||||
|
||||
public DecodeException(final String pMessage) {
|
||||
super(pMessage);
|
||||
}
|
||||
|
||||
public DecodeException(final String pMessage, final Throwable pCause) {
|
||||
super(pMessage);
|
||||
initCause(pCause);
|
||||
}
|
||||
|
||||
public DecodeException(final Throwable pCause) {
|
||||
this(pCause.getMessage(), pCause);
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class DecodeException extends IOException {
|
||||
|
||||
public DecodeException(final String pMessage) {
|
||||
super(pMessage);
|
||||
}
|
||||
|
||||
public DecodeException(final String pMessage, final Throwable pCause) {
|
||||
super(pMessage);
|
||||
initCause(pCause);
|
||||
}
|
||||
|
||||
public DecodeException(final Throwable pCause) {
|
||||
this(pCause.getMessage(), pCause);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Interface for decoders.
|
||||
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
|
||||
* on-the-fly decoding from an {@code InputStream}.
|
||||
* <p/>
|
||||
* Important note: Decoder implementations are typically not synchronized.
|
||||
* <p/>
|
||||
* @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 $
|
||||
*/
|
||||
public interface Decoder {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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 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;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Interface for decoders.
|
||||
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
|
||||
* on-the-fly decoding from an {@code InputStream}.
|
||||
* <p>
|
||||
* Important note: Decoder implementations are typically not synchronized.
|
||||
* </p>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface Decoder {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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 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;
|
||||
}
|
||||
|
||||
@@ -1,200 +1,199 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An {@code InputStream} that provides on-the-fly decoding from an underlying
|
||||
* stream.
|
||||
* <p/>
|
||||
* @see EncoderStream
|
||||
* @see Decoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class DecoderStream extends FilterInputStream {
|
||||
protected final ByteBuffer buffer;
|
||||
protected final Decoder decoder;
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
* The stream will use a default decode buffer size.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
|
||||
// TODO: Let the decoder decide preferred buffer size
|
||||
this(pStream, pDecoder, 1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
* @param pBufferSize the size of the decode buffer
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||
super(pStream);
|
||||
|
||||
decoder = pDecoder;
|
||||
buffer = ByteBuffer.allocate(pBufferSize);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return buffer.remaining();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.get() & 0xff;
|
||||
}
|
||||
|
||||
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// End of file?
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Read until we have read pLength bytes, or have reached EOF
|
||||
int count = 0;
|
||||
int off = pOffset;
|
||||
|
||||
while (pLength > count) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy as many bytes as possible
|
||||
int dstLen = Math.min(pLength - count, buffer.remaining());
|
||||
buffer.get(pBytes, off, dstLen);
|
||||
|
||||
// Update offset (rest)
|
||||
off += dstLen;
|
||||
|
||||
// Increase count
|
||||
count += dstLen;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public long skip(final long pLength) throws IOException {
|
||||
// End of file?
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
return 0; // Yes, 0, not -1
|
||||
}
|
||||
}
|
||||
|
||||
// Skip until we have skipped pLength bytes, or have reached EOF
|
||||
long total = 0;
|
||||
|
||||
while (total < pLength) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
buffer.position(buffer.position() + skipped);
|
||||
total += skipped;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* file is reached
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected int fill() throws IOException {
|
||||
buffer.clear();
|
||||
int read = decoder.decode(in, buffer);
|
||||
|
||||
// TODO: Enforce this in test case, leave here to aid debugging
|
||||
if (read > buffer.capacity()) {
|
||||
throw new AssertionError(
|
||||
String.format(
|
||||
"Decode beyond buffer (%d): %d (using %s decoder)",
|
||||
buffer.capacity(), read, decoder.getClass().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
|
||||
if (read == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An {@code InputStream} that provides on-the-fly decoding from an underlying stream.
|
||||
*
|
||||
* @see EncoderStream
|
||||
* @see Decoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class DecoderStream extends FilterInputStream {
|
||||
protected final ByteBuffer buffer;
|
||||
protected final Decoder decoder;
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
* The stream will use a default decode buffer size.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
|
||||
// TODO: Let the decoder decide preferred buffer size
|
||||
this(pStream, pDecoder, 1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
* @param pBufferSize the size of the decode buffer
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||
super(pStream);
|
||||
|
||||
decoder = pDecoder;
|
||||
buffer = ByteBuffer.allocate(pBufferSize);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return buffer.remaining();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.get() & 0xff;
|
||||
}
|
||||
|
||||
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// End of file?
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Read until we have read pLength bytes, or have reached EOF
|
||||
int count = 0;
|
||||
int off = pOffset;
|
||||
|
||||
while (pLength > count) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy as many bytes as possible
|
||||
int dstLen = Math.min(pLength - count, buffer.remaining());
|
||||
buffer.get(pBytes, off, dstLen);
|
||||
|
||||
// Update offset (rest)
|
||||
off += dstLen;
|
||||
|
||||
// Increase count
|
||||
count += dstLen;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public long skip(final long pLength) throws IOException {
|
||||
// End of file?
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
return 0; // Yes, 0, not -1
|
||||
}
|
||||
}
|
||||
|
||||
// Skip until we have skipped pLength bytes, or have reached EOF
|
||||
long total = 0;
|
||||
|
||||
while (total < pLength) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
buffer.position(buffer.position() + skipped);
|
||||
total += skipped;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* file is reached
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected int fill() throws IOException {
|
||||
buffer.clear();
|
||||
int read = decoder.decode(in, buffer);
|
||||
|
||||
// TODO: Enforce this in test case, leave here to aid debugging
|
||||
if (read > buffer.capacity()) {
|
||||
throw new AssertionError(
|
||||
String.format(
|
||||
"Decode beyond buffer (%d): %d (using %s decoder)",
|
||||
buffer.capacity(), read, decoder.getClass().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
|
||||
if (read == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Interface for encoders.
|
||||
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
|
||||
* on-the-fly encoding to an {@code OutputStream}.
|
||||
* <p/>
|
||||
* Important note: Encoder implementations are typically not synchronized.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface Encoder {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @throws java.io.IOException if an I/O error occurs
|
||||
*/
|
||||
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
|
||||
|
||||
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
|
||||
// void flush()?
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Interface for encoders.
|
||||
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
|
||||
* on-the-fly encoding to an {@code OutputStream}.
|
||||
* <p>
|
||||
* Important note: Encoder implementations are typically not synchronized.
|
||||
* </p>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface Encoder {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @throws java.io.IOException if an I/O error occurs
|
||||
*/
|
||||
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
|
||||
|
||||
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
|
||||
// void flush()?
|
||||
}
|
||||
|
||||
@@ -1,137 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An {@code OutputStream} that provides on-the-fly encoding to an underlying
|
||||
* stream.
|
||||
* <p/>
|
||||
* @see DecoderStream
|
||||
* @see Encoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class EncoderStream extends FilterOutputStream {
|
||||
// TODO: This class need a test case ASAP!!!
|
||||
|
||||
protected final Encoder encoder;
|
||||
private final boolean flushOnWrite;
|
||||
|
||||
protected final ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
*/
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
||||
this(pStream, pEncoder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
||||
* {@code write} methods will automatically flush the buffer.
|
||||
*/
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
||||
super(pStream);
|
||||
|
||||
encoder = pEncoder;
|
||||
flushOnWrite = pFlushOnWrite;
|
||||
|
||||
buffer = ByteBuffer.allocate(1024);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
encodeBuffer();
|
||||
super.flush();
|
||||
}
|
||||
|
||||
private void encodeBuffer() throws IOException {
|
||||
if (buffer.position() != 0) {
|
||||
buffer.flip();
|
||||
|
||||
// Make sure all remaining data in buffer is written to the stream
|
||||
encoder.encode(out, buffer);
|
||||
|
||||
// Reset buffer
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public final void write(final byte[] pBytes) throws IOException {
|
||||
write(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
// 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
|
||||
// that the encoder can't buffer. In that case, the encoder should probably
|
||||
// tell the EncoderStream how large buffer it prefers...
|
||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (!flushOnWrite && pLength < buffer.remaining()) {
|
||||
// Buffer data
|
||||
buffer.put(pBytes, pOffset, pLength);
|
||||
}
|
||||
else {
|
||||
// Encode data already in the buffer
|
||||
encodeBuffer();
|
||||
|
||||
// Encode rest without buffering
|
||||
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final int pByte) throws IOException {
|
||||
if (!buffer.hasRemaining()) {
|
||||
encodeBuffer(); // Resets bufferPos to 0
|
||||
}
|
||||
|
||||
buffer.put((byte) pByte);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An {@code OutputStream} that provides on-the-fly encoding to an underlying stream.
|
||||
*
|
||||
* @see DecoderStream
|
||||
* @see Encoder
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class EncoderStream extends FilterOutputStream {
|
||||
// TODO: This class need a test case ASAP!!!
|
||||
|
||||
protected final Encoder encoder;
|
||||
private final boolean flushOnWrite;
|
||||
|
||||
protected final ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
*/
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
||||
this(pStream, pEncoder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
||||
* {@code write} methods will automatically flush the buffer.
|
||||
*/
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
||||
super(pStream);
|
||||
|
||||
encoder = pEncoder;
|
||||
flushOnWrite = pFlushOnWrite;
|
||||
|
||||
buffer = ByteBuffer.allocate(1024);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
encodeBuffer();
|
||||
super.flush();
|
||||
}
|
||||
|
||||
private void encodeBuffer() throws IOException {
|
||||
if (buffer.position() != 0) {
|
||||
buffer.flip();
|
||||
|
||||
// Make sure all remaining data in buffer is written to the stream
|
||||
encoder.encode(out, buffer);
|
||||
|
||||
// Reset buffer
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public final void write(final byte[] pBytes) throws IOException {
|
||||
write(pBytes, 0, pBytes.length);
|
||||
}
|
||||
|
||||
// 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
|
||||
// that the encoder can't buffer. In that case, the encoder should probably
|
||||
// tell the EncoderStream how large buffer it prefers...
|
||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (!flushOnWrite && pLength < buffer.remaining()) {
|
||||
// Buffer data
|
||||
buffer.put(pBytes, pOffset, pLength);
|
||||
}
|
||||
else {
|
||||
// Encode data already in the buffer
|
||||
encodeBuffer();
|
||||
|
||||
// Encode rest without buffering
|
||||
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final int pByte) throws IOException {
|
||||
if (!buffer.hasRemaining()) {
|
||||
encodeBuffer(); // Resets bufferPos to 0
|
||||
}
|
||||
|
||||
buffer.put((byte) pByte);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,30 +37,36 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Decoder implementation for Apple PackBits run-length encoding.
|
||||
* <p/>
|
||||
* <small>From Wikipedia, the free encyclopedia</small><br/>
|
||||
* <p>
|
||||
* <small>From Wikipedia, the free encyclopedia</small>
|
||||
* <br>
|
||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||
* data.
|
||||
* <p/>
|
||||
* </p>
|
||||
* <p>
|
||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||
* Macintosh computer. This compression scheme is one of the types of
|
||||
* 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
|
||||
* data. The header is a signed byte; the data can be signed, unsigned, or
|
||||
* packed (such as MacPaint pixels).
|
||||
* <p/>
|
||||
* <table><tr><th>Header byte</th><th>Data</th></tr>
|
||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
|
||||
* the decompressed output</td></tr>
|
||||
* <tr><td>-128</td> <td>No operation</td></tr></table>
|
||||
* <p/>
|
||||
* </p>
|
||||
* <table>
|
||||
* <caption>PackBits</caption>
|
||||
* <tr><th>Header byte</th><th>Data</th></tr>
|
||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
|
||||
* <tr><td>-128</td> <td>No operation</td></tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* 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
|
||||
* literal data.
|
||||
* <p/>
|
||||
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
||||
* </p>
|
||||
*
|
||||
* @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>
|
||||
* @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.
|
||||
* <p/>
|
||||
* <p>
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
@@ -93,10 +100,11 @@ public final class PackBitsDecoder implements Decoder {
|
||||
|
||||
/**
|
||||
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
||||
* <p/>
|
||||
* <p>
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -1,132 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Encoder implementation for Apple PackBits run-length encoding.
|
||||
* <p/>
|
||||
* From Wikipedia, the free encyclopedia<br/>
|
||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||
* data.
|
||||
* <p/>
|
||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||
* Macintosh computer. This compression scheme is one of the types of
|
||||
* compression that can be used in TIFF-files.
|
||||
* <p/>
|
||||
* 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
|
||||
* packed (such as MacPaint pixels).
|
||||
* <p/>
|
||||
* <table><tr><th>Header byte</th><th>Data</th></tr>
|
||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
|
||||
* the decompressed output</td></tr>
|
||||
* <tr><td>-128</td> <td>No operation</td></tr></table>
|
||||
* <p/>
|
||||
* 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
|
||||
* literal data.
|
||||
* <p/>
|
||||
* 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>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
|
||||
*/
|
||||
public final class PackBitsEncoder implements Encoder {
|
||||
|
||||
final private byte[] buffer = new byte[128];
|
||||
|
||||
/**
|
||||
* Creates a {@code PackBitsEncoder}.
|
||||
*/
|
||||
public PackBitsEncoder() {
|
||||
}
|
||||
|
||||
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
|
||||
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||
buffer.position(buffer.remaining());
|
||||
}
|
||||
|
||||
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
||||
// NOTE: It's best to encode a 2 byte repeat
|
||||
// run as a replicate run except when preceded and followed by a
|
||||
// literal run, in which case it's best to merge the three into one
|
||||
// literal run. Always encode 3 byte repeats as replicate runs.
|
||||
// NOTE: Worst case: output = input + (input + 127) / 128
|
||||
|
||||
int offset = pOffset;
|
||||
final int max = pOffset + pLength - 1;
|
||||
final int maxMinus1 = max - 1;
|
||||
|
||||
while (offset <= max) {
|
||||
// Compressed run
|
||||
int run = 1;
|
||||
byte replicate = pBuffer[offset];
|
||||
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
|
||||
offset++;
|
||||
run++;
|
||||
}
|
||||
|
||||
if (run > 1) {
|
||||
offset++;
|
||||
pStream.write(-(run - 1));
|
||||
pStream.write(replicate);
|
||||
}
|
||||
|
||||
// Literal run
|
||||
run = 0;
|
||||
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|
||||
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
|
||||
buffer[run++] = pBuffer[offset++];
|
||||
}
|
||||
|
||||
// If last byte, include it in literal run, if space
|
||||
if (offset == max && run > 0 && run < 128) {
|
||||
buffer[run++] = pBuffer[offset++];
|
||||
}
|
||||
|
||||
if (run > 0) {
|
||||
pStream.write(run - 1);
|
||||
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++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.enc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Encoder implementation for Apple PackBits run-length encoding.
|
||||
* <p>
|
||||
* From Wikipedia, the free encyclopedia
|
||||
* <br>
|
||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||
* data.
|
||||
* </p>
|
||||
* <p>
|
||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||
* Macintosh computer. This compression scheme is one of the types of
|
||||
* compression that can be used in TIFF-files.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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
|
||||
* packed (such as MacPaint pixels).
|
||||
* </p>
|
||||
* <table>
|
||||
* <caption>PackBits</caption>
|
||||
* <tr><th>Header byte</th><th>Data</th></tr>
|
||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
|
||||
* <tr><td>-128</td> <td>No operation</td></tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* 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
|
||||
* literal data.
|
||||
* </p>
|
||||
*
|
||||
* @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>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
|
||||
*/
|
||||
public final class PackBitsEncoder implements Encoder {
|
||||
|
||||
final private byte[] buffer = new byte[128];
|
||||
|
||||
/**
|
||||
* Creates a {@code PackBitsEncoder}.
|
||||
*/
|
||||
public PackBitsEncoder() {
|
||||
}
|
||||
|
||||
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
|
||||
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||
buffer.position(buffer.remaining());
|
||||
}
|
||||
|
||||
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
||||
// NOTE: It's best to encode a 2 byte repeat
|
||||
// run as a replicate run except when preceded and followed by a
|
||||
// literal run, in which case it's best to merge the three into one
|
||||
// literal run. Always encode 3 byte repeats as replicate runs.
|
||||
// NOTE: Worst case: output = input + (input + 127) / 128
|
||||
|
||||
int offset = pOffset;
|
||||
final int max = pOffset + pLength - 1;
|
||||
final int maxMinus1 = max - 1;
|
||||
|
||||
while (offset <= max) {
|
||||
// Compressed run
|
||||
int run = 1;
|
||||
byte replicate = pBuffer[offset];
|
||||
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
|
||||
offset++;
|
||||
run++;
|
||||
}
|
||||
|
||||
if (run > 1) {
|
||||
offset++;
|
||||
pStream.write(-(run - 1));
|
||||
pStream.write(replicate);
|
||||
}
|
||||
|
||||
// Literal run
|
||||
run = 0;
|
||||
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|
||||
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
|
||||
buffer[run++] = pBuffer[offset++];
|
||||
}
|
||||
|
||||
// If last byte, include it in literal run, if space
|
||||
if (offset == max && run > 0 && run < 128) {
|
||||
buffer[run++] = pBuffer[offset++];
|
||||
}
|
||||
|
||||
if (run > 0) {
|
||||
pStream.write(run - 1);
|
||||
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++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,342 +1,344 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.ole2;
|
||||
|
||||
import com.twelvemonkeys.io.SeekableInputStream;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.no">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/io/ole2/Entry.java#4 $
|
||||
* @see com.twelvemonkeys.io.ole2.CompoundDocument
|
||||
*/
|
||||
// TODO: Consider extending java.io.File...
|
||||
public final class Entry implements Comparable<Entry> {
|
||||
String name;
|
||||
byte type;
|
||||
byte nodeColor;
|
||||
|
||||
int prevDId;
|
||||
int nextDId;
|
||||
int rootNodeDId;
|
||||
|
||||
long createdTimestamp;
|
||||
long modifiedTimestamp;
|
||||
|
||||
int startSId;
|
||||
int streamSize;
|
||||
|
||||
CompoundDocument document;
|
||||
Entry parent;
|
||||
SortedSet<Entry> children;
|
||||
|
||||
public final static int LENGTH = 128;
|
||||
|
||||
static final int EMPTY = 0;
|
||||
static final int USER_STORAGE = 1;
|
||||
static final int USER_STREAM = 2;
|
||||
static final int LOCK_BYTES = 3;
|
||||
static final int PROPERTY = 4;
|
||||
static final int ROOT_STORAGE = 5;
|
||||
|
||||
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
|
||||
|
||||
private Entry() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an entry from the input.
|
||||
*
|
||||
* @param pInput the input data
|
||||
* @return the {@code Entry} read from the input data
|
||||
* @throws IOException if an i/o exception occurs during reading
|
||||
*/
|
||||
static Entry readEntry(final DataInput pInput) throws IOException {
|
||||
Entry p = new Entry();
|
||||
p.read(pInput);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this entry
|
||||
*
|
||||
* @param pInput the input data
|
||||
* @throws IOException if an i/o exception occurs during reading
|
||||
*/
|
||||
private void read(final DataInput pInput) throws IOException {
|
||||
byte[] bytes = new byte[64];
|
||||
pInput.readFully(bytes);
|
||||
|
||||
// NOTE: Length is in bytes, including the null-terminator...
|
||||
int nameLength = pInput.readShort();
|
||||
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
|
||||
// System.out.println("name: " + name);
|
||||
|
||||
type = pInput.readByte();
|
||||
// System.out.println("type: " + type);
|
||||
|
||||
nodeColor = pInput.readByte();
|
||||
// System.out.println("nodeColor: " + nodeColor);
|
||||
|
||||
prevDId = pInput.readInt();
|
||||
// System.out.println("prevDId: " + prevDId);
|
||||
nextDId = pInput.readInt();
|
||||
// System.out.println("nextDId: " + nextDId);
|
||||
rootNodeDId = pInput.readInt();
|
||||
// System.out.println("rootNodeDId: " + rootNodeDId);
|
||||
|
||||
// UID (16) + user flags (4), ignored
|
||||
if (pInput.skipBytes(20) != 20) {
|
||||
throw new CorruptDocumentException();
|
||||
}
|
||||
|
||||
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||
|
||||
startSId = pInput.readInt();
|
||||
// System.out.println("startSId: " + startSId);
|
||||
streamSize = pInput.readInt();
|
||||
// System.out.println("streamSize: " + streamSize);
|
||||
|
||||
// Reserved
|
||||
pInput.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} this {@code Entry} is the root {@code Entry}.
|
||||
*
|
||||
* @return {@code true} if this is the root {@code Entry}
|
||||
*/
|
||||
public boolean isRoot() {
|
||||
return type == ROOT_STORAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} this {@code Entry} is a directory
|
||||
* {@code Entry}.
|
||||
*
|
||||
* @return {@code true} if this is a directory {@code Entry}
|
||||
*/
|
||||
public boolean isDirectory() {
|
||||
return type == USER_STORAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} this {@code Entry} is a file (document)
|
||||
* {@code Entry}.
|
||||
*
|
||||
* @return {@code true} if this is a document {@code Entry}
|
||||
*/
|
||||
public boolean isFile() {
|
||||
return type == USER_STREAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this {@code Entry}
|
||||
*
|
||||
* @return the name of this {@code Entry}
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code InputStream} for this {@code Entry}
|
||||
*
|
||||
* @return an {@code InputStream} containing the data for this
|
||||
* {@code Entry} or {@code null} if this is a directory {@code Entry}
|
||||
* @throws java.io.IOException if an I/O exception occurs
|
||||
* @see #length()
|
||||
*/
|
||||
public SeekableInputStream getInputStream() throws IOException {
|
||||
if (!isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return document.getInputStreamForSId(startSId, streamSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of this entry
|
||||
*
|
||||
* @return the length of the stream for this entry, or {@code 0} if this is
|
||||
* a directory {@code Entry}
|
||||
* @see #getInputStream()
|
||||
*/
|
||||
public long length() {
|
||||
if (!isFile()) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return streamSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time that this entry was created.
|
||||
* The time is converted from its internal representation to standard Java
|
||||
* representation, milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970).
|
||||
* <p/>
|
||||
* Note that most applications leaves this value empty ({@code 0L}).
|
||||
*
|
||||
* @return A {@code long} value representing the time this entry was
|
||||
* created, measured in milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
||||
* creation time stamp exists for this entry.
|
||||
*/
|
||||
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
|
||||
* representation, milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970).
|
||||
* <p/>
|
||||
* Note that many applications leaves this value empty ({@code 0L}).
|
||||
*
|
||||
* @return A {@code long} value representing the time this entry was
|
||||
* last modified, measured in milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
||||
* modification time stamp exists for this entry.
|
||||
*/
|
||||
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
|
||||
* the root {@code Entry}
|
||||
*/
|
||||
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}
|
||||
* @return the child {@code Entry} or {@code null} if thee is no such
|
||||
* child
|
||||
* @throws java.io.IOException if an I/O exception occurs
|
||||
*/
|
||||
public Entry getChildEntry(final String pName) throws IOException {
|
||||
if (isFile() || rootNodeDId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Entry dummy = new Entry();
|
||||
dummy.name = pName;
|
||||
dummy.parent = this;
|
||||
|
||||
SortedSet child = getChildEntries().tailSet(dummy);
|
||||
return (Entry) child.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the children of this {@code Entry}.
|
||||
*
|
||||
* @return a {@code SortedSet} of {@code Entry} objects
|
||||
* @throws java.io.IOException if an I/O exception occurs
|
||||
*/
|
||||
public SortedSet<Entry> getChildEntries() throws IOException {
|
||||
if (children == null) {
|
||||
if (isFile() || rootNodeDId == -1) {
|
||||
children = NO_CHILDREN;
|
||||
}
|
||||
else {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "\"" + name + "\""
|
||||
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
|
||||
+ (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) {
|
||||
return true;
|
||||
}
|
||||
if (!(pOther instanceof Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
int diff = name.length() - pOther.name.length();
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
return name.compareTo(pOther.name);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.io.ole2;
|
||||
|
||||
import com.twelvemonkeys.io.SeekableInputStream;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.no">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/io/ole2/Entry.java#4 $
|
||||
* @see com.twelvemonkeys.io.ole2.CompoundDocument
|
||||
*/
|
||||
// TODO: Consider extending java.io.File...
|
||||
public final class Entry implements Comparable<Entry> {
|
||||
String name;
|
||||
byte type;
|
||||
byte nodeColor;
|
||||
|
||||
int prevDId;
|
||||
int nextDId;
|
||||
int rootNodeDId;
|
||||
|
||||
long createdTimestamp;
|
||||
long modifiedTimestamp;
|
||||
|
||||
int startSId;
|
||||
int streamSize;
|
||||
|
||||
CompoundDocument document;
|
||||
Entry parent;
|
||||
SortedSet<Entry> children;
|
||||
|
||||
public final static int LENGTH = 128;
|
||||
|
||||
static final int EMPTY = 0;
|
||||
static final int USER_STORAGE = 1;
|
||||
static final int USER_STREAM = 2;
|
||||
static final int LOCK_BYTES = 3;
|
||||
static final int PROPERTY = 4;
|
||||
static final int ROOT_STORAGE = 5;
|
||||
|
||||
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
|
||||
|
||||
private Entry() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an entry from the input.
|
||||
*
|
||||
* @param pInput the input data
|
||||
* @return the {@code Entry} read from the input data
|
||||
* @throws IOException if an i/o exception occurs during reading
|
||||
*/
|
||||
static Entry readEntry(final DataInput pInput) throws IOException {
|
||||
Entry p = new Entry();
|
||||
p.read(pInput);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this entry
|
||||
*
|
||||
* @param pInput the input data
|
||||
* @throws IOException if an i/o exception occurs during reading
|
||||
*/
|
||||
private void read(final DataInput pInput) throws IOException {
|
||||
byte[] bytes = new byte[64];
|
||||
pInput.readFully(bytes);
|
||||
|
||||
// NOTE: Length is in bytes, including the null-terminator...
|
||||
int nameLength = pInput.readShort();
|
||||
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
|
||||
// System.out.println("name: " + name);
|
||||
|
||||
type = pInput.readByte();
|
||||
// System.out.println("type: " + type);
|
||||
|
||||
nodeColor = pInput.readByte();
|
||||
// System.out.println("nodeColor: " + nodeColor);
|
||||
|
||||
prevDId = pInput.readInt();
|
||||
// System.out.println("prevDId: " + prevDId);
|
||||
nextDId = pInput.readInt();
|
||||
// System.out.println("nextDId: " + nextDId);
|
||||
rootNodeDId = pInput.readInt();
|
||||
// System.out.println("rootNodeDId: " + rootNodeDId);
|
||||
|
||||
// UID (16) + user flags (4), ignored
|
||||
if (pInput.skipBytes(20) != 20) {
|
||||
throw new CorruptDocumentException();
|
||||
}
|
||||
|
||||
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||
|
||||
startSId = pInput.readInt();
|
||||
// System.out.println("startSId: " + startSId);
|
||||
streamSize = pInput.readInt();
|
||||
// System.out.println("streamSize: " + streamSize);
|
||||
|
||||
// Reserved
|
||||
pInput.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} this {@code Entry} is the root {@code Entry}.
|
||||
*
|
||||
* @return {@code true} if this is the root {@code Entry}
|
||||
*/
|
||||
public boolean isRoot() {
|
||||
return type == ROOT_STORAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} this {@code Entry} is a directory
|
||||
* {@code Entry}.
|
||||
*
|
||||
* @return {@code true} if this is a directory {@code Entry}
|
||||
*/
|
||||
public boolean isDirectory() {
|
||||
return type == USER_STORAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} this {@code Entry} is a file (document)
|
||||
* {@code Entry}.
|
||||
*
|
||||
* @return {@code true} if this is a document {@code Entry}
|
||||
*/
|
||||
public boolean isFile() {
|
||||
return type == USER_STREAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this {@code Entry}
|
||||
*
|
||||
* @return the name of this {@code Entry}
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code InputStream} for this {@code Entry}
|
||||
*
|
||||
* @return an {@code InputStream} containing the data for this
|
||||
* {@code Entry} or {@code null} if this is a directory {@code Entry}
|
||||
* @throws java.io.IOException if an I/O exception occurs
|
||||
* @see #length()
|
||||
*/
|
||||
public SeekableInputStream getInputStream() throws IOException {
|
||||
if (!isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return document.getInputStreamForSId(startSId, streamSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of this entry
|
||||
*
|
||||
* @return the length of the stream for this entry, or {@code 0} if this is
|
||||
* a directory {@code Entry}
|
||||
* @see #getInputStream()
|
||||
*/
|
||||
public long length() {
|
||||
if (!isFile()) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return streamSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time that this entry was created.
|
||||
* The time is converted from its internal representation to standard Java
|
||||
* representation, milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970).
|
||||
* <p>
|
||||
* 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
|
||||
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
||||
* creation time stamp exists for this entry.
|
||||
*/
|
||||
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
|
||||
* representation, milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970).
|
||||
* <p>
|
||||
* Note that many applications leaves this value empty ({@code 0L}).
|
||||
* </p>
|
||||
*
|
||||
* @return A {@code long} value representing the time this entry was
|
||||
* last modified, measured in milliseconds since the epoch
|
||||
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
||||
* modification time stamp exists for this entry.
|
||||
*/
|
||||
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
|
||||
* the root {@code Entry}
|
||||
*/
|
||||
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}
|
||||
* @return the child {@code Entry} or {@code null} if thee is no such
|
||||
* child
|
||||
* @throws java.io.IOException if an I/O exception occurs
|
||||
*/
|
||||
public Entry getChildEntry(final String pName) throws IOException {
|
||||
if (isFile() || rootNodeDId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Entry dummy = new Entry();
|
||||
dummy.name = pName;
|
||||
dummy.parent = this;
|
||||
|
||||
SortedSet child = getChildEntries().tailSet(dummy);
|
||||
return (Entry) child.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the children of this {@code Entry}.
|
||||
*
|
||||
* @return a {@code SortedSet} of {@code Entry} objects
|
||||
* @throws java.io.IOException if an I/O exception occurs
|
||||
*/
|
||||
public SortedSet<Entry> getChildEntries() throws IOException {
|
||||
if (children == null) {
|
||||
if (isFile() || rootNodeDId == -1) {
|
||||
children = NO_CHILDREN;
|
||||
}
|
||||
else {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "\"" + name + "\""
|
||||
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
|
||||
+ (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) {
|
||||
return true;
|
||||
}
|
||||
if (!(pOther instanceof Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
int diff = name.length() - pOther.name.length();
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
return name.compareTo(pOther.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,314 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.net;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @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>
|
||||
*/
|
||||
public final class MIMEUtil {
|
||||
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with 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/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
|
||||
// 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
|
||||
|
||||
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>> sMIMEToExt = new HashMap<String, List<String>>();
|
||||
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
|
||||
|
||||
static {
|
||||
// Load mapping for MIMEUtil
|
||||
try {
|
||||
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
|
||||
|
||||
for (Map.Entry entry : mappings.entrySet()) {
|
||||
// Convert and break up extensions and mimeTypes
|
||||
String extStr = StringUtil.toLowerCase((String) entry.getKey());
|
||||
List<String> extensions =
|
||||
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
|
||||
|
||||
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
|
||||
List<String> mimeTypes =
|
||||
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
|
||||
|
||||
// TODO: Handle duplicates in MIME to extension mapping, like
|
||||
// xhtml=application/xhtml+xml;application/xml
|
||||
// xml=text/xml;application/xml
|
||||
|
||||
// Populate normal and reverse MIME-mappings
|
||||
for (String extension : extensions) {
|
||||
sExtToMIME.put(extension, mimeTypes);
|
||||
}
|
||||
|
||||
for (String mimeType : mimeTypes) {
|
||||
sMIMEToExt.put(mimeType, extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow construction
|
||||
private MIMEUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default MIME type for the given file extension.
|
||||
*
|
||||
* @param pFileExt the file extension
|
||||
*
|
||||
* @return a {@code String} containing the MIME type, or {@code null} if
|
||||
* there are no known MIME types for the given file extension.
|
||||
*/
|
||||
public static String getMIMEType(final String pFileExt) {
|
||||
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
||||
return (types == null || types.isEmpty()) ? null : types.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all MIME types for the given file extension.
|
||||
*
|
||||
* @param pFileExt the file extension
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static List<String> getMIMETypes(final String pFileExt) {
|
||||
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
||||
return maskNull(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiabale {@link Map} view of the extension to
|
||||
* MIME mapping, to use as the default mapping in client applications.
|
||||
*
|
||||
* @return an unmodifiabale {@code Map} view of the extension to
|
||||
* MIME mapping.
|
||||
*/
|
||||
public static Map<String, List<String>> getMIMETypeMappings() {
|
||||
return sUnmodifiableExtToMIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default file extension for the given MIME type.
|
||||
* Specifying a wildcard type will return {@code null}.
|
||||
*
|
||||
* @param pMIME the MIME type
|
||||
*
|
||||
* @return a {@code String} containing the file extension, or {@code null}
|
||||
* if there are no known file extensions for the given MIME type.
|
||||
*/
|
||||
public static String getExtension(final String pMIME) {
|
||||
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
||||
List<String> extensions = sMIMEToExt.get(mime);
|
||||
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all file extension for the given MIME type.
|
||||
* The default extension will be the first in the list.
|
||||
* Note that no specific order is given for wildcard types (image/*, */* etc).
|
||||
*
|
||||
* @param pMIME the MIME type
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static List<String> getExtensions(final String pMIME) {
|
||||
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
||||
if (mime.endsWith("/*")) {
|
||||
return getExtensionForWildcard(mime);
|
||||
}
|
||||
List<String> extensions = sMIMEToExt.get(mime);
|
||||
return maskNull(extensions);
|
||||
}
|
||||
|
||||
// Gets all extensions for a wildcard MIME type
|
||||
private static List<String> getExtensionForWildcard(final String pMIME) {
|
||||
final String family = pMIME.substring(0, pMIME.length() - 1);
|
||||
Set<String> extensions = new LinkedHashSet<String>();
|
||||
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
|
||||
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
|
||||
extensions.addAll(mimeToExt.getValue());
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(new ArrayList<String>(extensions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiabale {@link Map} view of the MIME to
|
||||
* extension mapping, to use as the default mapping in client applications.
|
||||
*
|
||||
* @return an unmodifiabale {@code Map} view of the MIME to
|
||||
* extension mapping.
|
||||
*/
|
||||
public static Map<String, List<String>> getExtensionMappings() {
|
||||
return sUnmodifiableMIMEToExt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests wehter the type is a subtype of the type family.
|
||||
*
|
||||
* @param pTypeFamily the MIME type family ({@code image/*, */*}, etc)
|
||||
* @param pType the MIME type
|
||||
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
|
||||
*/
|
||||
// TODO: Rename? isSubtype?
|
||||
// TODO: Make public
|
||||
static boolean includes(final String pTypeFamily, final String pType) {
|
||||
// TODO: Handle null in a well-defined way
|
||||
// - Is null family same as */*?
|
||||
// - Is null subtype of any family? Subtype of no family?
|
||||
|
||||
String type = bareMIME(pType);
|
||||
return type.equals(pTypeFamily)
|
||||
|| "*/*".equals(pTypeFamily)
|
||||
|| 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).
|
||||
*
|
||||
* @param pMIME the mime-type string
|
||||
* @return the bare mime-type
|
||||
*/
|
||||
public static String bareMIME(final String pMIME) {
|
||||
int idx;
|
||||
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
|
||||
return pMIME.substring(0, idx);
|
||||
}
|
||||
return pMIME;
|
||||
}
|
||||
|
||||
// Returns the list or empty list if list is null
|
||||
private static List<String> maskNull(List<String> pTypes) {
|
||||
return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging. Prints all known MIME types and file extensions.
|
||||
*
|
||||
* @param pArgs command line arguments
|
||||
*/
|
||||
public static void main(String[] pArgs) {
|
||||
if (pArgs.length > 1) {
|
||||
String type = pArgs[0];
|
||||
String family = pArgs[1];
|
||||
boolean incuded = includes(family, type);
|
||||
System.out.println(
|
||||
"Mime type family " + family
|
||||
+ (incuded ? " includes " : " does not include ")
|
||||
+ "type " + type
|
||||
);
|
||||
}
|
||||
if (pArgs.length > 0) {
|
||||
String str = pArgs[0];
|
||||
|
||||
if (str.indexOf('/') >= 0) {
|
||||
// MIME
|
||||
String extension = getExtension(str);
|
||||
System.out.println("Default extension for MIME type '" + str + "' is "
|
||||
+ (extension != null ? ": '" + extension + "'" : "unknown") + ".");
|
||||
System.out.println("All possible: " + getExtensions(str));
|
||||
}
|
||||
else {
|
||||
// EXT
|
||||
String mimeType = getMIMEType(str);
|
||||
System.out.println("Default MIME type for extension '" + str + "' is "
|
||||
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
|
||||
System.out.println("All possible: " + getMIMETypes(str));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Set set = sMIMEToExt.keySet();
|
||||
String[] mimeTypes = new String[set.size()];
|
||||
int i = 0;
|
||||
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
||||
String mime = (String) iterator.next();
|
||||
mimeTypes[i] = mime;
|
||||
}
|
||||
Arrays.sort(mimeTypes);
|
||||
|
||||
System.out.println("Known MIME types (" + mimeTypes.length + "):");
|
||||
for (int j = 0; j < mimeTypes.length; j++) {
|
||||
String mimeType = mimeTypes[j];
|
||||
|
||||
if (j != 0) {
|
||||
System.out.print(", ");
|
||||
}
|
||||
|
||||
System.out.print(mimeType);
|
||||
}
|
||||
|
||||
System.out.println("\n");
|
||||
|
||||
set = sExtToMIME.keySet();
|
||||
String[] extensions = new String[set.size()];
|
||||
i = 0;
|
||||
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
||||
String ext = (String) iterator.next();
|
||||
extensions[i] = ext;
|
||||
}
|
||||
Arrays.sort(extensions);
|
||||
|
||||
System.out.println("Known file types (" + extensions.length + "):");
|
||||
for (int j = 0; j < extensions.length; j++) {
|
||||
String extension = extensions[j];
|
||||
|
||||
if (j != 0) {
|
||||
System.out.print(", ");
|
||||
}
|
||||
|
||||
System.out.print(extension);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.net;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Contains mappings from file extension to mime-types and from mime-type to file-types.
|
||||
*
|
||||
* @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 $
|
||||
*
|
||||
* @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A>
|
||||
*/
|
||||
public final class MIMEUtil {
|
||||
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with 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/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
|
||||
// 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
|
||||
|
||||
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>> sMIMEToExt = new HashMap<String, List<String>>();
|
||||
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
|
||||
|
||||
static {
|
||||
// Load mapping for MIMEUtil
|
||||
try {
|
||||
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
|
||||
|
||||
for (Map.Entry entry : mappings.entrySet()) {
|
||||
// Convert and break up extensions and mimeTypes
|
||||
String extStr = StringUtil.toLowerCase((String) entry.getKey());
|
||||
List<String> extensions =
|
||||
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
|
||||
|
||||
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
|
||||
List<String> mimeTypes =
|
||||
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
|
||||
|
||||
// TODO: Handle duplicates in MIME to extension mapping, like
|
||||
// xhtml=application/xhtml+xml;application/xml
|
||||
// xml=text/xml;application/xml
|
||||
|
||||
// Populate normal and reverse MIME-mappings
|
||||
for (String extension : extensions) {
|
||||
sExtToMIME.put(extension, mimeTypes);
|
||||
}
|
||||
|
||||
for (String mimeType : mimeTypes) {
|
||||
sMIMEToExt.put(mimeType, extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow construction
|
||||
private MIMEUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default MIME type for the given file extension.
|
||||
*
|
||||
* @param pFileExt the file extension
|
||||
*
|
||||
* @return a {@code String} containing the MIME type, or {@code null} if
|
||||
* there are no known MIME types for the given file extension.
|
||||
*/
|
||||
public static String getMIMEType(final String pFileExt) {
|
||||
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
||||
return (types == null || types.isEmpty()) ? null : types.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all MIME types for the given file extension.
|
||||
*
|
||||
* @param pFileExt the file extension
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static List<String> getMIMETypes(final String pFileExt) {
|
||||
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
||||
return maskNull(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiabale {@link Map} view of the extension to
|
||||
* MIME mapping, to use as the default mapping in client applications.
|
||||
*
|
||||
* @return an unmodifiabale {@code Map} view of the extension to
|
||||
* MIME mapping.
|
||||
*/
|
||||
public static Map<String, List<String>> getMIMETypeMappings() {
|
||||
return sUnmodifiableExtToMIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default file extension for the given MIME type.
|
||||
* Specifying a wildcard type will return {@code null}.
|
||||
*
|
||||
* @param pMIME the MIME type
|
||||
*
|
||||
* @return a {@code String} containing the file extension, or {@code null}
|
||||
* if there are no known file extensions for the given MIME type.
|
||||
*/
|
||||
public static String getExtension(final String pMIME) {
|
||||
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
||||
List<String> extensions = sMIMEToExt.get(mime);
|
||||
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all file extension for the given MIME type.
|
||||
* The default extension will be the first in the list.
|
||||
* Note that no specific order is given for wildcard types (image/*, */* etc).
|
||||
*
|
||||
* @param pMIME the MIME type
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static List<String> getExtensions(final String pMIME) {
|
||||
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
||||
if (mime.endsWith("/*")) {
|
||||
return getExtensionForWildcard(mime);
|
||||
}
|
||||
List<String> extensions = sMIMEToExt.get(mime);
|
||||
return maskNull(extensions);
|
||||
}
|
||||
|
||||
// Gets all extensions for a wildcard MIME type
|
||||
private static List<String> getExtensionForWildcard(final String pMIME) {
|
||||
final String family = pMIME.substring(0, pMIME.length() - 1);
|
||||
Set<String> extensions = new LinkedHashSet<String>();
|
||||
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
|
||||
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
|
||||
extensions.addAll(mimeToExt.getValue());
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(new ArrayList<String>(extensions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiabale {@link Map} view of the MIME to
|
||||
* extension mapping, to use as the default mapping in client applications.
|
||||
*
|
||||
* @return an unmodifiabale {@code Map} view of the MIME to
|
||||
* extension mapping.
|
||||
*/
|
||||
public static Map<String, List<String>> getExtensionMappings() {
|
||||
return sUnmodifiableMIMEToExt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests wehter the type is a subtype of the type family.
|
||||
*
|
||||
* @param pTypeFamily the MIME type family ({@code image/*, */*}, etc)
|
||||
* @param pType the MIME type
|
||||
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
|
||||
*/
|
||||
// TODO: Rename? isSubtype?
|
||||
// TODO: Make public
|
||||
static boolean includes(final String pTypeFamily, final String pType) {
|
||||
// TODO: Handle null in a well-defined way
|
||||
// - Is null family same as */*?
|
||||
// - Is null subtype of any family? Subtype of no family?
|
||||
|
||||
String type = bareMIME(pType);
|
||||
return type.equals(pTypeFamily)
|
||||
|| "*/*".equals(pTypeFamily)
|
||||
|| 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).
|
||||
*
|
||||
* @param pMIME the mime-type string
|
||||
* @return the bare mime-type
|
||||
*/
|
||||
public static String bareMIME(final String pMIME) {
|
||||
int idx;
|
||||
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
|
||||
return pMIME.substring(0, idx);
|
||||
}
|
||||
return pMIME;
|
||||
}
|
||||
|
||||
// Returns the list or empty list if list is null
|
||||
private static List<String> maskNull(List<String> pTypes) {
|
||||
return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging. Prints all known MIME types and file extensions.
|
||||
*
|
||||
* @param pArgs command line arguments
|
||||
*/
|
||||
public static void main(String[] pArgs) {
|
||||
if (pArgs.length > 1) {
|
||||
String type = pArgs[0];
|
||||
String family = pArgs[1];
|
||||
boolean incuded = includes(family, type);
|
||||
System.out.println(
|
||||
"Mime type family " + family
|
||||
+ (incuded ? " includes " : " does not include ")
|
||||
+ "type " + type
|
||||
);
|
||||
}
|
||||
if (pArgs.length > 0) {
|
||||
String str = pArgs[0];
|
||||
|
||||
if (str.indexOf('/') >= 0) {
|
||||
// MIME
|
||||
String extension = getExtension(str);
|
||||
System.out.println("Default extension for MIME type '" + str + "' is "
|
||||
+ (extension != null ? ": '" + extension + "'" : "unknown") + ".");
|
||||
System.out.println("All possible: " + getExtensions(str));
|
||||
}
|
||||
else {
|
||||
// EXT
|
||||
String mimeType = getMIMEType(str);
|
||||
System.out.println("Default MIME type for extension '" + str + "' is "
|
||||
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
|
||||
System.out.println("All possible: " + getMIMETypes(str));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Set set = sMIMEToExt.keySet();
|
||||
String[] mimeTypes = new String[set.size()];
|
||||
int i = 0;
|
||||
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
||||
String mime = (String) iterator.next();
|
||||
mimeTypes[i] = mime;
|
||||
}
|
||||
Arrays.sort(mimeTypes);
|
||||
|
||||
System.out.println("Known MIME types (" + mimeTypes.length + "):");
|
||||
for (int j = 0; j < mimeTypes.length; j++) {
|
||||
String mimeType = mimeTypes[j];
|
||||
|
||||
if (j != 0) {
|
||||
System.out.print(", ");
|
||||
}
|
||||
|
||||
System.out.print(mimeType);
|
||||
}
|
||||
|
||||
System.out.println("\n");
|
||||
|
||||
set = sExtToMIME.keySet();
|
||||
String[] extensions = new String[set.size()];
|
||||
i = 0;
|
||||
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
||||
String ext = (String) iterator.next();
|
||||
extensions[i] = ext;
|
||||
}
|
||||
Arrays.sort(extensions);
|
||||
|
||||
System.out.println("Known file types (" + extensions.length + "):");
|
||||
for (int j = 0; j < extensions.length; j++) {
|
||||
String extension = extensions[j];
|
||||
|
||||
if (j != 0) {
|
||||
System.out.print(", ");
|
||||
}
|
||||
|
||||
System.out.print(extension);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
@@ -102,8 +102,9 @@ public final class DOMSerializer {
|
||||
/**
|
||||
* Specifies wether the serializer should use indentation and optimize for
|
||||
* readability.
|
||||
* <p/>
|
||||
* Note: This is a hint, and may be ignored by DOM implemenations.
|
||||
* <p>
|
||||
* Note: This is a hint, and may be ignored by DOM implementations.
|
||||
* </p>
|
||||
*
|
||||
* @param pPrettyPrint {@code true} to enable pretty printing
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,204 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* A utility class with useful date manipulation methods and constants.
|
||||
* <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/DateUtil.java#1 $
|
||||
*/
|
||||
public final class DateUtil {
|
||||
|
||||
/** One second: 1000 milliseconds. */
|
||||
public static final long SECOND = 1000l;
|
||||
|
||||
/** One minute: 60 seconds (60 000 milliseconds). */
|
||||
public static final long MINUTE = 60l * SECOND;
|
||||
|
||||
/**
|
||||
* One hour: 60 minutes (3 600 000 milliseconds).
|
||||
* 60 minutes = 3 600 seconds = 3 600 000 milliseconds
|
||||
*/
|
||||
public static final long HOUR = 60l * MINUTE;
|
||||
|
||||
/**
|
||||
* One day: 24 hours (86 400 000 milliseconds).
|
||||
* 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
|
||||
*/
|
||||
public static final long DAY = 24l * HOUR;
|
||||
|
||||
/**
|
||||
* One calendar year: 365.2425 days (31556952000 milliseconds).
|
||||
* 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
|
||||
* = 31556952000 milliseconds.
|
||||
*/
|
||||
public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
|
||||
|
||||
private DateUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time between the given start time and now (as defined by
|
||||
* {@link System#currentTimeMillis()}).
|
||||
*
|
||||
* @param pStart the start time
|
||||
*
|
||||
* @return the time between the given start time and now.
|
||||
*/
|
||||
public static long delta(long pStart) {
|
||||
return System.currentTimeMillis() - pStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time between the given start time and now (as defined by
|
||||
* {@link System#currentTimeMillis()}).
|
||||
*
|
||||
* @param pStart the start time
|
||||
*
|
||||
* @return the time between the given start time and now.
|
||||
*/
|
||||
public static long delta(Date pStart) {
|
||||
return System.currentTimeMillis() - pStart.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest second.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToSecond(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest second.
|
||||
*/
|
||||
public static long currentTimeSecond() {
|
||||
return roundToSecond(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest minute.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToMinute(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest minute.
|
||||
*/
|
||||
public static long currentTimeMinute() {
|
||||
return roundToMinute(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest hour.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToHour(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest hour.
|
||||
*/
|
||||
public static long currentTimeHour() {
|
||||
return roundToHour(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest day.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToDay(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest day.
|
||||
*/
|
||||
public static long currentTimeDay() {
|
||||
return roundToDay(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest second.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest second.
|
||||
*/
|
||||
public static long roundToSecond(final long pTime) {
|
||||
return (pTime / SECOND) * SECOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest minute.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest minute.
|
||||
*/
|
||||
public static long roundToMinute(final long pTime) {
|
||||
return (pTime / MINUTE) * MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest hour, using the default timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest hour.
|
||||
*/
|
||||
public static long roundToHour(final long pTime) {
|
||||
return roundToHour(pTime, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest hour, using the given timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @param pTimeZone the timezone to use when rounding
|
||||
* @return the time rounded to the closest hour.
|
||||
*/
|
||||
public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
|
||||
int offset = pTimeZone.getOffset(pTime);
|
||||
return ((pTime / HOUR) * HOUR) - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest day, using the default timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest day.
|
||||
*/
|
||||
public static long roundToDay(final long pTime) {
|
||||
return roundToDay(pTime, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest day, using the given timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @param pTimeZone the timezone to use when rounding
|
||||
* @return the time rounded to the closest day.
|
||||
*/
|
||||
public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
|
||||
int offset = pTimeZone.getOffset(pTime);
|
||||
return (((pTime + offset) / DAY) * DAY) - offset;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* A utility class with useful date manipulation methods and constants.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class DateUtil {
|
||||
|
||||
/** One second: 1000 milliseconds. */
|
||||
public static final long SECOND = 1000l;
|
||||
|
||||
/** One minute: 60 seconds (60 000 milliseconds). */
|
||||
public static final long MINUTE = 60l * SECOND;
|
||||
|
||||
/**
|
||||
* One hour: 60 minutes (3 600 000 milliseconds).
|
||||
* 60 minutes = 3 600 seconds = 3 600 000 milliseconds
|
||||
*/
|
||||
public static final long HOUR = 60l * MINUTE;
|
||||
|
||||
/**
|
||||
* One day: 24 hours (86 400 000 milliseconds).
|
||||
* 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
|
||||
*/
|
||||
public static final long DAY = 24l * HOUR;
|
||||
|
||||
/**
|
||||
* One calendar year: 365.2425 days (31556952000 milliseconds).
|
||||
* 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
|
||||
* = 31556952000 milliseconds.
|
||||
*/
|
||||
public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
|
||||
|
||||
private DateUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time between the given start time and now (as defined by
|
||||
* {@link System#currentTimeMillis()}).
|
||||
*
|
||||
* @param pStart the start time
|
||||
*
|
||||
* @return the time between the given start time and now.
|
||||
*/
|
||||
public static long delta(long pStart) {
|
||||
return System.currentTimeMillis() - pStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time between the given start time and now (as defined by
|
||||
* {@link System#currentTimeMillis()}).
|
||||
*
|
||||
* @param pStart the start time
|
||||
*
|
||||
* @return the time between the given start time and now.
|
||||
*/
|
||||
public static long delta(Date pStart) {
|
||||
return System.currentTimeMillis() - pStart.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest second.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToSecond(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest second.
|
||||
*/
|
||||
public static long currentTimeSecond() {
|
||||
return roundToSecond(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest minute.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToMinute(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest minute.
|
||||
*/
|
||||
public static long currentTimeMinute() {
|
||||
return roundToMinute(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest hour.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToHour(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest hour.
|
||||
*/
|
||||
public static long currentTimeHour() {
|
||||
return roundToHour(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time, rounded down to the closest day.
|
||||
* Equivalent to invoking
|
||||
* {@code roundToDay(System.currentTimeMillis())}.
|
||||
*
|
||||
* @return the current time, rounded to the closest day.
|
||||
*/
|
||||
public static long currentTimeDay() {
|
||||
return roundToDay(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest second.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest second.
|
||||
*/
|
||||
public static long roundToSecond(final long pTime) {
|
||||
return (pTime / SECOND) * SECOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest minute.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest minute.
|
||||
*/
|
||||
public static long roundToMinute(final long pTime) {
|
||||
return (pTime / MINUTE) * MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest hour, using the default timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest hour.
|
||||
*/
|
||||
public static long roundToHour(final long pTime) {
|
||||
return roundToHour(pTime, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest hour, using the given timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @param pTimeZone the timezone to use when rounding
|
||||
* @return the time rounded to the closest hour.
|
||||
*/
|
||||
public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
|
||||
int offset = pTimeZone.getOffset(pTime);
|
||||
return ((pTime / HOUR) * HOUR) - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest day, using the default timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest day.
|
||||
*/
|
||||
public static long roundToDay(final long pTime) {
|
||||
return roundToDay(pTime, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest day, using the given timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @param pTimeZone the timezone to use when rounding
|
||||
* @return the time rounded to the closest day.
|
||||
*/
|
||||
public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
|
||||
int offset = pTimeZone.getOffset(pTime);
|
||||
return (((pTime + offset) / DAY) * DAY) - offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,9 +199,10 @@ public final class Platform {
|
||||
|
||||
/**
|
||||
* Enumeration of common System {@code Architecture}s.
|
||||
* <p/>
|
||||
* <p>
|
||||
* For {@link #Unknown unknown architectures}, {@code toString()} will return
|
||||
* the the same value as {@code System.getProperty("os.arch")}.
|
||||
* </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/Platform.java#1 $
|
||||
@@ -228,9 +229,10 @@ public final class Platform {
|
||||
|
||||
/**
|
||||
* Enumeration of common {@code OperatingSystem}s.
|
||||
* <p/>
|
||||
* <p>
|
||||
* For {@link #Unknown unknown operating systems}, {@code getName()} will return
|
||||
* the the same value as {@code System.getProperty("os.name")}.
|
||||
* </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/Platform.java#1 $
|
||||
|
||||
@@ -1,139 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
/**
|
||||
* Util class for various reflection-based operations.
|
||||
* <p/>
|
||||
* <em>NOTE: This class is not considered part of the public API and may be
|
||||
* changed without notice</em>
|
||||
*
|
||||
* @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 {
|
||||
|
||||
/** Don't allow instances */
|
||||
private ReflectUtil() {}
|
||||
|
||||
/**
|
||||
* Returns the primitive type for the given wrapper type.
|
||||
*
|
||||
* @param pType the wrapper type
|
||||
*
|
||||
* @return the primitive type
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pType} is not a primitive
|
||||
* wrapper
|
||||
*/
|
||||
public static Class unwrapType(Class pType) {
|
||||
if (pType == Boolean.class) {
|
||||
return Boolean.TYPE;
|
||||
}
|
||||
else if (pType == Byte.class) {
|
||||
return Byte.TYPE;
|
||||
}
|
||||
else if (pType == Character.class) {
|
||||
return Character.TYPE;
|
||||
}
|
||||
else if (pType == Double.class) {
|
||||
return Double.TYPE;
|
||||
}
|
||||
else if (pType == Float.class) {
|
||||
return Float.TYPE;
|
||||
}
|
||||
else if (pType == Integer.class) {
|
||||
return Integer.TYPE;
|
||||
}
|
||||
else if (pType == Long.class) {
|
||||
return Long.TYPE;
|
||||
}
|
||||
else if (pType == Short.class) {
|
||||
return Short.TYPE;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapper type for the given primitive type.
|
||||
*
|
||||
* @param pType the primitive tpye
|
||||
*
|
||||
* @return the wrapper type
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pType} is not a primitive
|
||||
* type
|
||||
*/
|
||||
public static Class wrapType(Class pType) {
|
||||
if (pType == Boolean.TYPE) {
|
||||
return Boolean.class;
|
||||
}
|
||||
else if (pType == Byte.TYPE) {
|
||||
return Byte.class;
|
||||
}
|
||||
else if (pType == Character.TYPE) {
|
||||
return Character.class;
|
||||
}
|
||||
else if (pType == Double.TYPE) {
|
||||
return Double.class;
|
||||
}
|
||||
else if (pType == Float.TYPE) {
|
||||
return Float.class;
|
||||
}
|
||||
else if (pType == Integer.TYPE) {
|
||||
return Integer.class;
|
||||
}
|
||||
else if (pType == Long.TYPE) {
|
||||
return Long.class;
|
||||
}
|
||||
else if (pType == Short.TYPE) {
|
||||
return Short.class;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a primitive type: " + pType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given type is a primitive wrapper.
|
||||
*
|
||||
* @param pType
|
||||
*
|
||||
* @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
|
||||
|| pType == Character.class || pType == Double.class
|
||||
|| pType == Float.class || pType == Integer.class
|
||||
|| pType == Long.class || pType == Short.class;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
/**
|
||||
* Util class for various reflection-based operations.
|
||||
* <p>
|
||||
* <em>NOTE: This class is not considered part of the public API and may be
|
||||
* 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 $
|
||||
*/
|
||||
public final class ReflectUtil {
|
||||
|
||||
/** Don't allow instances */
|
||||
private ReflectUtil() {}
|
||||
|
||||
/**
|
||||
* Returns the primitive type for the given wrapper type.
|
||||
*
|
||||
* @param pType the wrapper type
|
||||
*
|
||||
* @return the primitive type
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pType} is not a primitive
|
||||
* wrapper
|
||||
*/
|
||||
public static Class unwrapType(Class pType) {
|
||||
if (pType == Boolean.class) {
|
||||
return Boolean.TYPE;
|
||||
}
|
||||
else if (pType == Byte.class) {
|
||||
return Byte.TYPE;
|
||||
}
|
||||
else if (pType == Character.class) {
|
||||
return Character.TYPE;
|
||||
}
|
||||
else if (pType == Double.class) {
|
||||
return Double.TYPE;
|
||||
}
|
||||
else if (pType == Float.class) {
|
||||
return Float.TYPE;
|
||||
}
|
||||
else if (pType == Integer.class) {
|
||||
return Integer.TYPE;
|
||||
}
|
||||
else if (pType == Long.class) {
|
||||
return Long.TYPE;
|
||||
}
|
||||
else if (pType == Short.class) {
|
||||
return Short.TYPE;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapper type for the given primitive type.
|
||||
*
|
||||
* @param pType the primitive tpye
|
||||
*
|
||||
* @return the wrapper type
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pType} is not a primitive
|
||||
* type
|
||||
*/
|
||||
public static Class wrapType(Class pType) {
|
||||
if (pType == Boolean.TYPE) {
|
||||
return Boolean.class;
|
||||
}
|
||||
else if (pType == Byte.TYPE) {
|
||||
return Byte.class;
|
||||
}
|
||||
else if (pType == Character.TYPE) {
|
||||
return Character.class;
|
||||
}
|
||||
else if (pType == Double.TYPE) {
|
||||
return Double.class;
|
||||
}
|
||||
else if (pType == Float.TYPE) {
|
||||
return Float.class;
|
||||
}
|
||||
else if (pType == Integer.TYPE) {
|
||||
return Integer.class;
|
||||
}
|
||||
else if (pType == Long.TYPE) {
|
||||
return Long.class;
|
||||
}
|
||||
else if (pType == Short.TYPE) {
|
||||
return Short.class;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a primitive type: " + pType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given type is a primitive wrapper.
|
||||
*
|
||||
* @param pType
|
||||
*
|
||||
* @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
|
||||
|| pType == Character.class || pType == Double.class
|
||||
|| pType == Float.class || pType == Integer.class
|
||||
|| pType == Long.class || pType == Short.class;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -36,10 +36,11 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* Kind of like {@code org.apache.commons.lang.Validate}. Just smarter. ;-)
|
||||
* <p/>
|
||||
* <p>
|
||||
* Uses type parameterized return values, thus making it possible to check
|
||||
* constructor arguments before
|
||||
* 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 last modified by $Author: haku $
|
||||
|
||||
@@ -1,402 +1,404 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* AbstractDecoratedMap
|
||||
* <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/util/AbstractDecoratedMap.java#2 $
|
||||
*/
|
||||
// TODO: The generics in this class looks suspicious..
|
||||
abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable {
|
||||
protected Map<K, Entry<K, V>> entries;
|
||||
protected transient volatile int modCount;
|
||||
|
||||
private transient volatile Set<Entry<K, V>> entrySet = null;
|
||||
private transient volatile Set<K> keySet = null;
|
||||
private transient volatile Collection<V> values = null;
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} backed by a {@code HashMap}.
|
||||
*/
|
||||
public AbstractDecoratedMap() {
|
||||
this(new HashMap<K, Entry<K, V>>(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} backed by a {@code HashMap}, containing all
|
||||
* key/value mappings from the given {@code Map}.
|
||||
* <p/>
|
||||
* <small>This is constructor is here to comply with the reccomendations for
|
||||
* "standard" constructors in the {@code Map} interface.</small>
|
||||
*
|
||||
* @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
*/
|
||||
public AbstractDecoratedMap(Map<? extends K, ? extends V> pContents) {
|
||||
this(new HashMap<K, Entry<K, V>>(), pContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} backed by the given backing-{@code Map},
|
||||
* containing all key/value mappings from the given contents-{@code Map}.
|
||||
* <p/>
|
||||
* NOTE: The backing map is structuraly cahnged, and it should NOT be
|
||||
* accessed directly, after the wrapped map is created.
|
||||
*
|
||||
* @param pBacking the backing map of this map. Must be either empty, or
|
||||
* the same map as {@code pContents}.
|
||||
* @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.
|
||||
*/
|
||||
public AbstractDecoratedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
|
||||
if (pBacking == null) {
|
||||
throw new IllegalArgumentException("backing == null");
|
||||
}
|
||||
|
||||
Entry<? extends K, ? extends V>[] entries = null;
|
||||
if (pBacking == pContents) {
|
||||
// NOTE: Special treatment to avoid ClassCastExceptions
|
||||
Set<? extends Entry<? extends K, ? extends V>> es = pContents.entrySet();
|
||||
//noinspection unchecked
|
||||
entries = new Entry[es.size()];
|
||||
entries = es.toArray(entries);
|
||||
pContents = null;
|
||||
pBacking.clear();
|
||||
}
|
||||
else if (!pBacking.isEmpty()) {
|
||||
throw new IllegalArgumentException("backing must be empty");
|
||||
}
|
||||
|
||||
this.entries = pBacking;
|
||||
init();
|
||||
|
||||
if (pContents != null) {
|
||||
putAll(pContents);
|
||||
}
|
||||
else if (entries != null) {
|
||||
// Reinsert entries, this time wrapped
|
||||
for (Entry<? extends K, ? extends V> entry : entries) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation, does nothing.
|
||||
*/
|
||||
protected void init() {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
entries.clear();
|
||||
modCount++;
|
||||
init();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return entries.isEmpty();
|
||||
}
|
||||
|
||||
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
|
||||
* this map contains at least one mapping to a pValue {@code v} such that
|
||||
* {@code (pValue==null ? v==null : pValue.equals(v))}.
|
||||
* <p/>
|
||||
* This implementation requires time linear in the map size for this
|
||||
* operation.
|
||||
*
|
||||
* @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
|
||||
* specified pValue.
|
||||
*/
|
||||
public boolean containsValue(Object pValue) {
|
||||
for (V value : values()) {
|
||||
if (value == pValue || (value != null && value.equals(pValue))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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<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.
|
||||
*
|
||||
* @return a shallow copy of this map.
|
||||
*/
|
||||
protected Object clone() throws CloneNotSupportedException {
|
||||
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
|
||||
|
||||
map.values = null;
|
||||
map.entrySet = null;
|
||||
map.keySet = null;
|
||||
|
||||
// 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();
|
||||
|
||||
protected abstract Iterator<V> newValueIterator();
|
||||
|
||||
protected abstract Iterator<Entry<K, V>> newEntryIterator();
|
||||
|
||||
// TODO: Implement these (get/put/remove)?
|
||||
public abstract V get(Object pKey);
|
||||
|
||||
public abstract V remove(Object pKey);
|
||||
|
||||
public abstract V put(K pKey, V 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given entry from the Map.
|
||||
*
|
||||
* @param pEntry the entry to be removed
|
||||
*
|
||||
* @return the removed entry, or {@code null} if nothing was removed.
|
||||
*/
|
||||
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))) {
|
||||
// Remove
|
||||
remove(pEntry.getKey());
|
||||
return pEntry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected class Values extends AbstractCollection<V> {
|
||||
public Iterator<V> iterator() {
|
||||
return newValueIterator();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return AbstractDecoratedMap.this.size();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return containsValue(o);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
AbstractDecoratedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Entry e = (Entry) o;
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
/*
|
||||
// NOTE: Extra cautions is taken, to only remove the entry if it
|
||||
// equals the entry in the map
|
||||
Object key = ((Entry) o).getKey();
|
||||
Entry entry = (Entry) entries.get(key);
|
||||
|
||||
// Same entry?
|
||||
if (entry != null && entry.equals(o)) {
|
||||
return AbstractWrappedMap.this.remove(key) != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
*/
|
||||
|
||||
//noinspection unchecked
|
||||
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return AbstractDecoratedMap.this.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
AbstractDecoratedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected class KeySet extends AbstractSet<K> {
|
||||
public Iterator<K> iterator() {
|
||||
return newKeyIterator();
|
||||
}
|
||||
public int size() {
|
||||
return AbstractDecoratedMap.this.size();
|
||||
}
|
||||
public boolean contains(Object o) {
|
||||
return containsKey(o);
|
||||
}
|
||||
public boolean remove(Object o) {
|
||||
return AbstractDecoratedMap.this.remove(o) != null;
|
||||
}
|
||||
public void clear() {
|
||||
AbstractDecoratedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple Map.Entry implementaton.
|
||||
*/
|
||||
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
|
||||
K mKey;
|
||||
V mValue;
|
||||
|
||||
BasicEntry(K pKey, V pValue) {
|
||||
mKey = pKey;
|
||||
mValue = pValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation does nothing.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
protected void recordRemoval(Map<K, V> pMap) {
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public V setValue(V pValue) {
|
||||
V oldValue = mValue;
|
||||
mValue = pValue;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
public boolean equals(Object pOther) {
|
||||
if (!(pOther instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map.Entry entry = (Map.Entry) pOther;
|
||||
|
||||
Object k1 = mKey;
|
||||
Object k2 = entry.getKey();
|
||||
|
||||
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
|
||||
Object v1 = mValue;
|
||||
Object v2 = entry.getValue();
|
||||
|
||||
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (mKey == null ? 0 : mKey.hashCode()) ^
|
||||
(mValue == null ? 0 : mValue.hashCode());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getKey() + "=" + getValue();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* AbstractDecoratedMap
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
// TODO: The generics in this class looks suspicious..
|
||||
abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable {
|
||||
protected Map<K, Entry<K, V>> entries;
|
||||
protected transient volatile int modCount;
|
||||
|
||||
private transient volatile Set<Entry<K, V>> entrySet = null;
|
||||
private transient volatile Set<K> keySet = null;
|
||||
private transient volatile Collection<V> values = null;
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} backed by a {@code HashMap}.
|
||||
*/
|
||||
public AbstractDecoratedMap() {
|
||||
this(new HashMap<K, Entry<K, V>>(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} backed by a {@code HashMap}, containing all
|
||||
* key/value mappings from the given {@code Map}.
|
||||
* <p>
|
||||
* <small>This is constructor is here to comply with the reccomendations for
|
||||
* "standard" constructors in the {@code Map} interface.</small>
|
||||
* </p>
|
||||
*
|
||||
* @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
*/
|
||||
public AbstractDecoratedMap(Map<? extends K, ? extends V> pContents) {
|
||||
this(new HashMap<K, Entry<K, V>>(), pContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} backed by the given backing-{@code Map},
|
||||
* containing all key/value mappings from the given contents-{@code Map}.
|
||||
* <p>
|
||||
* NOTE: The backing map is structuraly cahnged, and it should NOT be
|
||||
* 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 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.
|
||||
*/
|
||||
public AbstractDecoratedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
|
||||
if (pBacking == null) {
|
||||
throw new IllegalArgumentException("backing == null");
|
||||
}
|
||||
|
||||
Entry<? extends K, ? extends V>[] entries = null;
|
||||
if (pBacking == pContents) {
|
||||
// NOTE: Special treatment to avoid ClassCastExceptions
|
||||
Set<? extends Entry<? extends K, ? extends V>> es = pContents.entrySet();
|
||||
//noinspection unchecked
|
||||
entries = new Entry[es.size()];
|
||||
entries = es.toArray(entries);
|
||||
pContents = null;
|
||||
pBacking.clear();
|
||||
}
|
||||
else if (!pBacking.isEmpty()) {
|
||||
throw new IllegalArgumentException("backing must be empty");
|
||||
}
|
||||
|
||||
this.entries = pBacking;
|
||||
init();
|
||||
|
||||
if (pContents != null) {
|
||||
putAll(pContents);
|
||||
}
|
||||
else if (entries != null) {
|
||||
// Reinsert entries, this time wrapped
|
||||
for (Entry<? extends K, ? extends V> entry : entries) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation, does nothing.
|
||||
*/
|
||||
protected void init() {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
entries.clear();
|
||||
modCount++;
|
||||
init();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return entries.isEmpty();
|
||||
}
|
||||
|
||||
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
|
||||
* this map contains at least one mapping to a pValue {@code v} such that
|
||||
* {@code (pValue==null ? v==null : pValue.equals(v))}.
|
||||
* <p>
|
||||
* This implementation requires time linear in the map size for this
|
||||
* operation.
|
||||
* </p>
|
||||
*
|
||||
* @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
|
||||
* specified pValue.
|
||||
*/
|
||||
public boolean containsValue(Object pValue) {
|
||||
for (V value : values()) {
|
||||
if (value == pValue || (value != null && value.equals(pValue))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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<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.
|
||||
*
|
||||
* @return a shallow copy of this map.
|
||||
*/
|
||||
protected Object clone() throws CloneNotSupportedException {
|
||||
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
|
||||
|
||||
map.values = null;
|
||||
map.entrySet = null;
|
||||
map.keySet = null;
|
||||
|
||||
// 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();
|
||||
|
||||
protected abstract Iterator<V> newValueIterator();
|
||||
|
||||
protected abstract Iterator<Entry<K, V>> newEntryIterator();
|
||||
|
||||
// TODO: Implement these (get/put/remove)?
|
||||
public abstract V get(Object pKey);
|
||||
|
||||
public abstract V remove(Object pKey);
|
||||
|
||||
public abstract V put(K pKey, V 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given entry from the Map.
|
||||
*
|
||||
* @param pEntry the entry to be removed
|
||||
*
|
||||
* @return the removed entry, or {@code null} if nothing was removed.
|
||||
*/
|
||||
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))) {
|
||||
// Remove
|
||||
remove(pEntry.getKey());
|
||||
return pEntry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected class Values extends AbstractCollection<V> {
|
||||
public Iterator<V> iterator() {
|
||||
return newValueIterator();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return AbstractDecoratedMap.this.size();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return containsValue(o);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
AbstractDecoratedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Entry e = (Entry) o;
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
/*
|
||||
// NOTE: Extra cautions is taken, to only remove the entry if it
|
||||
// equals the entry in the map
|
||||
Object key = ((Entry) o).getKey();
|
||||
Entry entry = (Entry) entries.get(key);
|
||||
|
||||
// Same entry?
|
||||
if (entry != null && entry.equals(o)) {
|
||||
return AbstractWrappedMap.this.remove(key) != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
*/
|
||||
|
||||
//noinspection unchecked
|
||||
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return AbstractDecoratedMap.this.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
AbstractDecoratedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected class KeySet extends AbstractSet<K> {
|
||||
public Iterator<K> iterator() {
|
||||
return newKeyIterator();
|
||||
}
|
||||
public int size() {
|
||||
return AbstractDecoratedMap.this.size();
|
||||
}
|
||||
public boolean contains(Object o) {
|
||||
return containsKey(o);
|
||||
}
|
||||
public boolean remove(Object o) {
|
||||
return AbstractDecoratedMap.this.remove(o) != null;
|
||||
}
|
||||
public void clear() {
|
||||
AbstractDecoratedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple Map.Entry implementaton.
|
||||
*/
|
||||
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
|
||||
K mKey;
|
||||
V mValue;
|
||||
|
||||
BasicEntry(K pKey, V pValue) {
|
||||
mKey = pKey;
|
||||
mValue = pValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation does nothing.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
protected void recordRemoval(Map<K, V> pMap) {
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public V setValue(V pValue) {
|
||||
V oldValue = mValue;
|
||||
mValue = pValue;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
public boolean equals(Object pOther) {
|
||||
if (!(pOther instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map.Entry entry = (Map.Entry) pOther;
|
||||
|
||||
Object k1 = mKey;
|
||||
Object k2 = entry.getKey();
|
||||
|
||||
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
|
||||
Object v1 = mValue;
|
||||
Object v2 = entry.getValue();
|
||||
|
||||
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (mKey == null ? 0 : mKey.hashCode()) ^
|
||||
(mValue == null ? 0 : mValue.hashCode());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getKey() + "=" + getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@code TokenIterator}s to extend.
|
||||
* <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/util/AbstractTokenIterator.java#1 $
|
||||
*/
|
||||
public abstract class AbstractTokenIterator implements TokenIterator {
|
||||
|
||||
/**
|
||||
* Not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException {@code remove} is not supported by
|
||||
* this Iterator.
|
||||
*/
|
||||
public void remove() {
|
||||
// TODO: This is not difficult:
|
||||
// - Convert String to StringBuilder in constructor
|
||||
// - delete(pos, next.lenght())
|
||||
// - Add toString() method
|
||||
// BUT: Would it ever be useful? :-)
|
||||
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
|
||||
public final boolean hasMoreTokens() {
|
||||
return hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration as a {@code String}.
|
||||
* This implementation simply returns {@code (String) next()}.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @exception java.util.NoSuchElementException iteration has no more elements.
|
||||
* @see #next()
|
||||
*/
|
||||
public final String nextToken() {
|
||||
return next();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation simply returns {@code hasNext()}.
|
||||
* @see #hasNext()
|
||||
*/
|
||||
public final boolean hasMoreElements() {
|
||||
return hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation simply returns {@code next()}.
|
||||
* @see #next()
|
||||
*/
|
||||
public final String nextElement() {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@code TokenIterator}s to extend.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public abstract class AbstractTokenIterator implements TokenIterator {
|
||||
|
||||
/**
|
||||
* Not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException {@code remove} is not supported by
|
||||
* this Iterator.
|
||||
*/
|
||||
public void remove() {
|
||||
// TODO: This is not difficult:
|
||||
// - Convert String to StringBuilder in constructor
|
||||
// - delete(pos, next.lenght())
|
||||
// - Add toString() method
|
||||
// BUT: Would it ever be useful? :-)
|
||||
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
|
||||
public final boolean hasMoreTokens() {
|
||||
return hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration as a {@code String}.
|
||||
* This implementation simply returns {@code (String) next()}.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @exception java.util.NoSuchElementException iteration has no more elements.
|
||||
* @see #next()
|
||||
*/
|
||||
public final String nextToken() {
|
||||
return next();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation simply returns {@code hasNext()}.
|
||||
* @see #hasNext()
|
||||
*/
|
||||
public final boolean hasMoreElements() {
|
||||
return hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation simply returns {@code next()}.
|
||||
* @see #next()
|
||||
*/
|
||||
public final String nextElement() {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +1,248 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.beans.IndexedPropertyDescriptor;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A {@code Map} adapter for a Java Bean.
|
||||
* <p/>
|
||||
* Ruthlessly stolen from
|
||||
* <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html>Binkley's Blog</a>
|
||||
*/
|
||||
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {
|
||||
private final Object bean;
|
||||
private transient Set<PropertyDescriptor> descriptors;
|
||||
|
||||
public BeanMap(Object pBean) throws IntrospectionException {
|
||||
if (pBean == null) {
|
||||
throw new IllegalArgumentException("bean == null");
|
||||
}
|
||||
|
||||
bean = pBean;
|
||||
descriptors = initDescriptors(pBean);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Skip Object.getClass(), as you probably never want it
|
||||
if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only support simple setter/getters.
|
||||
if (!(descriptor instanceof IndexedPropertyDescriptor)) {
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(descriptors);
|
||||
}
|
||||
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return new BeanSet();
|
||||
}
|
||||
|
||||
public Object get(final Object pKey) {
|
||||
return super.get(pKey);
|
||||
}
|
||||
|
||||
public Object put(final String pKey, final Object pValue) {
|
||||
checkKey(pKey);
|
||||
|
||||
for (Entry<String, Object> entry : entrySet()) {
|
||||
if (entry.getKey().equals(pKey)) {
|
||||
return entry.setValue(pValue);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object remove(final Object pKey) {
|
||||
return super.remove(checkKey(pKey));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return descriptors.size();
|
||||
}
|
||||
|
||||
private String checkKey(final Object pKey) {
|
||||
if (pKey == null) {
|
||||
throw new IllegalArgumentException("key == null");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private Object readResolve() throws IntrospectionException {
|
||||
// Initialize the property descriptors
|
||||
descriptors = initDescriptors(bean);
|
||||
return this;
|
||||
}
|
||||
|
||||
private class BeanSet extends AbstractSet<Entry<String, Object>> {
|
||||
public Iterator<Entry<String, Object>> iterator() {
|
||||
return new BeanIterator(descriptors.iterator());
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return descriptors.size();
|
||||
}
|
||||
}
|
||||
|
||||
private class BeanIterator implements Iterator<Entry<String, Object>> {
|
||||
private final Iterator<PropertyDescriptor> mIterator;
|
||||
|
||||
public BeanIterator(final Iterator<PropertyDescriptor> pIterator) {
|
||||
mIterator = pIterator;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
|
||||
public BeanEntry next() {
|
||||
return new BeanEntry(mIterator.next());
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
mIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private class BeanEntry implements Entry<String, Object> {
|
||||
private final PropertyDescriptor mDescriptor;
|
||||
|
||||
public BeanEntry(final PropertyDescriptor pDescriptor) {
|
||||
this.mDescriptor = pDescriptor;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return mDescriptor.getName();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return unwrap(new Wrapped() {
|
||||
public Object run() throws IllegalAccessException, InvocationTargetException {
|
||||
final Method method = mDescriptor.getReadMethod();
|
||||
// A write-only bean.
|
||||
if (method == null) {
|
||||
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
|
||||
}
|
||||
|
||||
return method.invoke(bean);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Object setValue(final Object pValue) {
|
||||
return unwrap(new Wrapped() {
|
||||
public Object run() throws IllegalAccessException, InvocationTargetException {
|
||||
final Method method = mDescriptor.getWriteMethod();
|
||||
// A read-only bean.
|
||||
if (method == null) {
|
||||
throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName());
|
||||
}
|
||||
|
||||
final Object old = getValue();
|
||||
method.invoke(bean, pValue);
|
||||
return old;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean equals(Object pOther) {
|
||||
if (!(pOther instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map.Entry entry = (Map.Entry) pOther;
|
||||
|
||||
Object k1 = getKey();
|
||||
Object k2 = entry.getKey();
|
||||
|
||||
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
|
||||
Object v1 = getValue();
|
||||
Object v2 = entry.getValue();
|
||||
|
||||
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (getKey() == null ? 0 : getKey().hashCode()) ^
|
||||
(getValue() == null ? 0 : getValue().hashCode());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getKey() + "=" + getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static interface Wrapped {
|
||||
Object run() throws IllegalAccessException, InvocationTargetException;
|
||||
}
|
||||
|
||||
private static Object unwrap(final Wrapped wrapped) {
|
||||
try {
|
||||
return wrapped.run();
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final InvocationTargetException e) {
|
||||
// Javadocs for setValue indicate cast is ok.
|
||||
throw (RuntimeException) e.getCause();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.beans.IndexedPropertyDescriptor;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A {@code Map} adapter for a Java Bean.
|
||||
* <p>
|
||||
* Ruthlessly stolen from
|
||||
* <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;
|
||||
private transient Set<PropertyDescriptor> descriptors;
|
||||
|
||||
public BeanMap(Object pBean) throws IntrospectionException {
|
||||
if (pBean == null) {
|
||||
throw new IllegalArgumentException("bean == null");
|
||||
}
|
||||
|
||||
bean = pBean;
|
||||
descriptors = initDescriptors(pBean);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Skip Object.getClass(), as you probably never want it
|
||||
if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only support simple setter/getters.
|
||||
if (!(descriptor instanceof IndexedPropertyDescriptor)) {
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(descriptors);
|
||||
}
|
||||
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return new BeanSet();
|
||||
}
|
||||
|
||||
public Object get(final Object pKey) {
|
||||
return super.get(pKey);
|
||||
}
|
||||
|
||||
public Object put(final String pKey, final Object pValue) {
|
||||
checkKey(pKey);
|
||||
|
||||
for (Entry<String, Object> entry : entrySet()) {
|
||||
if (entry.getKey().equals(pKey)) {
|
||||
return entry.setValue(pValue);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object remove(final Object pKey) {
|
||||
return super.remove(checkKey(pKey));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return descriptors.size();
|
||||
}
|
||||
|
||||
private String checkKey(final Object pKey) {
|
||||
if (pKey == null) {
|
||||
throw new IllegalArgumentException("key == null");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private Object readResolve() throws IntrospectionException {
|
||||
// Initialize the property descriptors
|
||||
descriptors = initDescriptors(bean);
|
||||
return this;
|
||||
}
|
||||
|
||||
private class BeanSet extends AbstractSet<Entry<String, Object>> {
|
||||
public Iterator<Entry<String, Object>> iterator() {
|
||||
return new BeanIterator(descriptors.iterator());
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return descriptors.size();
|
||||
}
|
||||
}
|
||||
|
||||
private class BeanIterator implements Iterator<Entry<String, Object>> {
|
||||
private final Iterator<PropertyDescriptor> mIterator;
|
||||
|
||||
public BeanIterator(final Iterator<PropertyDescriptor> pIterator) {
|
||||
mIterator = pIterator;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
|
||||
public BeanEntry next() {
|
||||
return new BeanEntry(mIterator.next());
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
mIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private class BeanEntry implements Entry<String, Object> {
|
||||
private final PropertyDescriptor mDescriptor;
|
||||
|
||||
public BeanEntry(final PropertyDescriptor pDescriptor) {
|
||||
this.mDescriptor = pDescriptor;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return mDescriptor.getName();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return unwrap(new Wrapped() {
|
||||
public Object run() throws IllegalAccessException, InvocationTargetException {
|
||||
final Method method = mDescriptor.getReadMethod();
|
||||
// A write-only bean.
|
||||
if (method == null) {
|
||||
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
|
||||
}
|
||||
|
||||
return method.invoke(bean);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Object setValue(final Object pValue) {
|
||||
return unwrap(new Wrapped() {
|
||||
public Object run() throws IllegalAccessException, InvocationTargetException {
|
||||
final Method method = mDescriptor.getWriteMethod();
|
||||
// A read-only bean.
|
||||
if (method == null) {
|
||||
throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName());
|
||||
}
|
||||
|
||||
final Object old = getValue();
|
||||
method.invoke(bean, pValue);
|
||||
return old;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean equals(Object pOther) {
|
||||
if (!(pOther instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map.Entry entry = (Map.Entry) pOther;
|
||||
|
||||
Object k1 = getKey();
|
||||
Object k2 = entry.getKey();
|
||||
|
||||
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
|
||||
Object v1 = getValue();
|
||||
Object v2 = entry.getValue();
|
||||
|
||||
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (getKey() == null ? 0 : getKey().hashCode()) ^
|
||||
(getValue() == null ? 0 : getValue().hashCode());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getKey() + "=" + getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static interface Wrapped {
|
||||
Object run() throws IllegalAccessException, InvocationTargetException;
|
||||
}
|
||||
|
||||
private static Object unwrap(final Wrapped wrapped) {
|
||||
try {
|
||||
return wrapped.run();
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final InvocationTargetException e) {
|
||||
// Javadocs for setValue indicate cast is ok.
|
||||
throw (RuntimeException) e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,153 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
/**
|
||||
* DuplicateHandler
|
||||
* <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/util/DuplicateHandler.java#2 $
|
||||
*/
|
||||
public interface DuplicateHandler<T> {
|
||||
|
||||
/**
|
||||
* Resolves duplicates according to a certain strategy.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return the resolved value.
|
||||
*
|
||||
* @throws IllegalArgumentException is the arguments cannot be resolved for
|
||||
* some reason.
|
||||
*/
|
||||
public T resolve(T pOld, T pNew);
|
||||
|
||||
/**
|
||||
* Will use the first (old) value. Any new values will be discarded.
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<?> USE_FIRST_VALUE = new DuplicateHandler() {
|
||||
/**
|
||||
* Returns {@code pOld}.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return {@code pOld}
|
||||
*/
|
||||
public Object resolve(Object pOld, Object pNew) {
|
||||
return pOld;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Will use the last (new) value. Any old values will be discarded
|
||||
* (overwritten).
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<?> USE_LAST_VALUE = new DuplicateHandler() {
|
||||
/**
|
||||
* Returns {@code pNew}.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return {@code pNew}
|
||||
*/
|
||||
public Object resolve(Object pOld, Object pNew) {
|
||||
return pNew;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts duplicats to an {@code Object} array.
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<?> DUPLICATES_AS_ARRAY = new DuplicateHandler() {
|
||||
/**
|
||||
* Returns an {@code Object} array, containing {@code pNew} as its
|
||||
* last element.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return an {@code Object} array, containing {@code pNew} as its
|
||||
* last element.
|
||||
*/
|
||||
public Object resolve(Object pOld, Object pNew) {
|
||||
Object[] result;
|
||||
|
||||
if (pOld instanceof Object[]) {
|
||||
Object[] old = ((Object[]) pOld);
|
||||
result = new Object[old.length + 1];
|
||||
System.arraycopy(old, 0, result, 0, old.length);
|
||||
result[old.length] = pNew;
|
||||
}
|
||||
else {
|
||||
result = new Object[] {pOld, pNew};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts duplicates to a comma-separated {@code String}.
|
||||
* Note that all values should allready be {@code String}s if using this
|
||||
* handler.
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<String> DUPLICATES_AS_CSV = new DuplicateHandler<String>() {
|
||||
/**
|
||||
* Returns a comma-separated {@code String}, with the string
|
||||
* representation of {@code pNew} as the last element.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return a comma-separated {@code String}, with the string
|
||||
* representation of {@code pNew} as the last element.
|
||||
*/
|
||||
public String resolve(String pOld, String pNew) {
|
||||
StringBuilder result = new StringBuilder(String.valueOf(pOld));
|
||||
result.append(',');
|
||||
result.append(pNew);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
/**
|
||||
* DuplicateHandler
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface DuplicateHandler<T> {
|
||||
|
||||
/**
|
||||
* Resolves duplicates according to a certain strategy.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return the resolved value.
|
||||
*
|
||||
* @throws IllegalArgumentException is the arguments cannot be resolved for
|
||||
* some reason.
|
||||
*/
|
||||
public T resolve(T pOld, T pNew);
|
||||
|
||||
/**
|
||||
* Will use the first (old) value. Any new values will be discarded.
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<?> USE_FIRST_VALUE = new DuplicateHandler() {
|
||||
/**
|
||||
* Returns {@code pOld}.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return {@code pOld}
|
||||
*/
|
||||
public Object resolve(Object pOld, Object pNew) {
|
||||
return pOld;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Will use the last (new) value. Any old values will be discarded
|
||||
* (overwritten).
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<?> USE_LAST_VALUE = new DuplicateHandler() {
|
||||
/**
|
||||
* Returns {@code pNew}.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return {@code pNew}
|
||||
*/
|
||||
public Object resolve(Object pOld, Object pNew) {
|
||||
return pNew;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts duplicats to an {@code Object} array.
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<?> DUPLICATES_AS_ARRAY = new DuplicateHandler() {
|
||||
/**
|
||||
* Returns an {@code Object} array, containing {@code pNew} as its
|
||||
* last element.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return an {@code Object} array, containing {@code pNew} as its
|
||||
* last element.
|
||||
*/
|
||||
public Object resolve(Object pOld, Object pNew) {
|
||||
Object[] result;
|
||||
|
||||
if (pOld instanceof Object[]) {
|
||||
Object[] old = ((Object[]) pOld);
|
||||
result = new Object[old.length + 1];
|
||||
System.arraycopy(old, 0, result, 0, old.length);
|
||||
result[old.length] = pNew;
|
||||
}
|
||||
else {
|
||||
result = new Object[] {pOld, pNew};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts duplicates to a comma-separated {@code String}.
|
||||
* Note that all values should allready be {@code String}s if using this
|
||||
* handler.
|
||||
*
|
||||
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||
*/
|
||||
public final static DuplicateHandler<String> DUPLICATES_AS_CSV = new DuplicateHandler<String>() {
|
||||
/**
|
||||
* Returns a comma-separated {@code String}, with the string
|
||||
* representation of {@code pNew} as the last element.
|
||||
*
|
||||
* @param pOld the old value
|
||||
* @param pNew the new value
|
||||
*
|
||||
* @return a comma-separated {@code String}, with the string
|
||||
* representation of {@code pNew} as the last element.
|
||||
*/
|
||||
public String resolve(String pOld, String pNew) {
|
||||
StringBuilder result = new StringBuilder(String.valueOf(pOld));
|
||||
result.append(',');
|
||||
result.append(pNew);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,153 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Wraps (decorates) an {@code Iterator} with extra functionality, to allow
|
||||
* element filtering. Each
|
||||
* element is filtered against the given {@code Filter}, and only elements
|
||||
* that are {@code accept}ed are returned by the {@code next} method.
|
||||
* <p/>
|
||||
* The optional {@code remove} operation is implemented, but may throw
|
||||
* {@code UnsupportedOperationException} if the underlying iterator does not
|
||||
* support the remove operation.
|
||||
*
|
||||
* @see FilterIterator.Filter
|
||||
*
|
||||
* @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/util/FilterIterator.java#1 $
|
||||
*/
|
||||
public class FilterIterator<E> implements Iterator<E> {
|
||||
|
||||
protected final Filter<E> filter;
|
||||
protected final Iterator<E> iterator;
|
||||
|
||||
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
|
||||
* that are {@code accept}ed are returned by the {@code next} method.
|
||||
*
|
||||
* @param pIterator the iterator to filter
|
||||
* @param pFilter the filter
|
||||
* @see FilterIterator.Filter
|
||||
*/
|
||||
public FilterIterator(final Iterator<E> pIterator, final Filter<E> pFilter) {
|
||||
if (pIterator == null) {
|
||||
throw new IllegalArgumentException("iterator == null");
|
||||
}
|
||||
if (pFilter == null) {
|
||||
throw new IllegalArgumentException("filter == null");
|
||||
}
|
||||
|
||||
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
|
||||
* rather than throwing an exception.)
|
||||
*
|
||||
* @return {@code true} if the iterator has more elements.
|
||||
* @see FilterIterator.Filter#accept
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
while (next == null && iterator.hasNext()) {
|
||||
E element = iterator.next();
|
||||
|
||||
if (filter.accept(element)) {
|
||||
next = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @see FilterIterator.Filter#accept
|
||||
*/
|
||||
public E next() {
|
||||
if (hasNext()) {
|
||||
current = next;
|
||||
|
||||
// Make sure we advance next time
|
||||
next = null;
|
||||
return current;
|
||||
}
|
||||
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
|
||||
* call to {@code next}. The behavior of an iterator is unspecified if
|
||||
* 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) {
|
||||
iterator.remove();
|
||||
}
|
||||
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.
|
||||
*/
|
||||
public static interface Filter<E> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code false}
|
||||
*/
|
||||
public boolean accept(E pElement);
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Wraps (decorates) an {@code Iterator} with extra functionality, to allow
|
||||
* element filtering. Each
|
||||
* element is filtered against the given {@code Filter}, and only elements
|
||||
* that are {@code accept}ed are returned by the {@code next} method.
|
||||
* <p>
|
||||
* The optional {@code remove} operation is implemented, but may throw
|
||||
* {@code UnsupportedOperationException} if the underlying iterator does not
|
||||
* support the remove operation.
|
||||
* </p>
|
||||
*
|
||||
* @see FilterIterator.Filter
|
||||
*
|
||||
* @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/util/FilterIterator.java#1 $
|
||||
*/
|
||||
public class FilterIterator<E> implements Iterator<E> {
|
||||
|
||||
protected final Filter<E> filter;
|
||||
protected final Iterator<E> iterator;
|
||||
|
||||
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
|
||||
* that are {@code accept}ed are returned by the {@code next} method.
|
||||
*
|
||||
* @param pIterator the iterator to filter
|
||||
* @param pFilter the filter
|
||||
* @see FilterIterator.Filter
|
||||
*/
|
||||
public FilterIterator(final Iterator<E> pIterator, final Filter<E> pFilter) {
|
||||
if (pIterator == null) {
|
||||
throw new IllegalArgumentException("iterator == null");
|
||||
}
|
||||
if (pFilter == null) {
|
||||
throw new IllegalArgumentException("filter == null");
|
||||
}
|
||||
|
||||
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
|
||||
* rather than throwing an exception.)
|
||||
*
|
||||
* @return {@code true} if the iterator has more elements.
|
||||
* @see FilterIterator.Filter#accept
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
while (next == null && iterator.hasNext()) {
|
||||
E element = iterator.next();
|
||||
|
||||
if (filter.accept(element)) {
|
||||
next = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @see FilterIterator.Filter#accept
|
||||
*/
|
||||
public E next() {
|
||||
if (hasNext()) {
|
||||
current = next;
|
||||
|
||||
// Make sure we advance next time
|
||||
next = null;
|
||||
return current;
|
||||
}
|
||||
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
|
||||
* call to {@code next}. The behavior of an iterator is unspecified if
|
||||
* 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) {
|
||||
iterator.remove();
|
||||
}
|
||||
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.
|
||||
*/
|
||||
public static interface Filter<E> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code false}
|
||||
*/
|
||||
public boolean accept(E pElement);
|
||||
}
|
||||
}
|
||||
@@ -1,178 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@code Map} decorator that makes the mappings in the backing map
|
||||
* case insensitive
|
||||
* (this is implemented by converting all keys to uppercase),
|
||||
* if the keys used are {@code Strings}. If the keys
|
||||
* used are not {@code String}s, it wil work as a normal
|
||||
* {@code java.util.Map}.
|
||||
* <p/>
|
||||
*
|
||||
* @see java.util.Map
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements Serializable, Cloneable {
|
||||
|
||||
/**
|
||||
* Constructs a new empty {@code Map}.
|
||||
* The backing map will be a {@link java.util.HashMap}
|
||||
*/
|
||||
public IgnoreCaseMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Map} with the same key-value mappings as the
|
||||
* given {@code Map}.
|
||||
* The backing map will be a {@link java.util.HashMap}
|
||||
* <p/>
|
||||
* NOTE: As the keys in the given map parameter will be converted to
|
||||
* uppercase (if they are strings), any duplicate key/value pair where
|
||||
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
|
||||
* is true, will be lost.
|
||||
*
|
||||
* @param pMap the map whose mappings are to be placed in this map.
|
||||
*/
|
||||
public IgnoreCaseMap(Map<String, ? extends V> pMap) {
|
||||
super(pMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Map} with the same key-value mappings as the
|
||||
* given {@code Map}.
|
||||
* <p/>
|
||||
* NOTE: The backing map is structuraly cahnged, and it should NOT be
|
||||
* accessed directly, after the wrapped map is created.
|
||||
* <p/>
|
||||
* NOTE: As the keys in the given map parameter will be converted to
|
||||
* uppercase (if they are strings), any duplicate key/value pair where
|
||||
* {@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
|
||||
* the same map as {@code pContents}.
|
||||
* @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}
|
||||
* @throws IllegalArgumentException if {@code pBacking} differs from
|
||||
* {@code pContent} and is not empty.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pKey the map key.
|
||||
* @param pValue the value.
|
||||
* @return the previous value of the specified key in this map,
|
||||
* or null if it did not have one.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param pKey a key in the map
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pKey the key that needs to be removed.
|
||||
* @return the value to which the key had been mapped in this map,
|
||||
* or null if the key did not have a mapping.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pKey possible key.
|
||||
* @return true if and only if the specified object is a key in this
|
||||
* map, as determined by the equals method; false otherwise.
|
||||
*/
|
||||
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) {
|
||||
if (pObject instanceof String) {
|
||||
return ((String) pObject).toUpperCase();
|
||||
}
|
||||
return pObject;
|
||||
}
|
||||
|
||||
protected Iterator<Entry<String, V>> newEntryIterator() {
|
||||
return (Iterator) entries.entrySet().iterator();
|
||||
}
|
||||
|
||||
protected Iterator<String> newKeyIterator() {
|
||||
return entries.keySet().iterator();
|
||||
}
|
||||
|
||||
protected Iterator<V> newValueIterator() {
|
||||
return (Iterator<V>) entries.values().iterator();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@code Map} decorator that makes the mappings in the backing map
|
||||
* case insensitive
|
||||
* (this is implemented by converting all keys to uppercase),
|
||||
* if the keys used are {@code Strings}. If the keys
|
||||
* used are not {@code String}s, it wil work as a normal
|
||||
* {@code java.util.Map}.
|
||||
*
|
||||
* @see java.util.Map
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements Serializable, Cloneable {
|
||||
|
||||
/**
|
||||
* Constructs a new empty {@code Map}.
|
||||
* The backing map will be a {@link java.util.HashMap}
|
||||
*/
|
||||
public IgnoreCaseMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Map} with the same key-value mappings as the
|
||||
* given {@code Map}.
|
||||
* The backing map will be a {@link java.util.HashMap}
|
||||
* <p>
|
||||
* NOTE: As the keys in the given map parameter will be converted to
|
||||
* uppercase (if they are strings), any duplicate key/value pair where
|
||||
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
|
||||
* is true, will be lost.
|
||||
* </p>
|
||||
*
|
||||
* @param pMap the map whose mappings are to be placed in this map.
|
||||
*/
|
||||
public IgnoreCaseMap(Map<String, ? extends V> pMap) {
|
||||
super(pMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Map} with the same key-value mappings as the
|
||||
* given {@code Map}.
|
||||
* <p>
|
||||
* NOTE: The backing map is structuraly cahnged, and it should NOT be
|
||||
* accessed directly, after the wrapped map is created.
|
||||
* </p>
|
||||
* <p>
|
||||
* NOTE: As the keys in the given map parameter will be converted to
|
||||
* uppercase (if they are strings), any duplicate key/value pair where
|
||||
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
|
||||
* is true, will be lost.
|
||||
* </p>
|
||||
*
|
||||
* @param pBacking the backing map of this map. Must be either empty, or
|
||||
* the same map as {@code pContents}.
|
||||
* @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}
|
||||
* @throws IllegalArgumentException if {@code pBacking} differs from
|
||||
* {@code pContent} and is not empty.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pKey the map key.
|
||||
* @param pValue the value.
|
||||
* @return the previous value of the specified key in this map,
|
||||
* or null if it did not have one.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param pKey a key in the map
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pKey the key that needs to be removed.
|
||||
* @return the value to which the key had been mapped in this map,
|
||||
* or null if the key did not have a mapping.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pKey possible key.
|
||||
* @return true if and only if the specified object is a key in this
|
||||
* map, as determined by the equals method; false otherwise.
|
||||
*/
|
||||
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) {
|
||||
if (pObject instanceof String) {
|
||||
return ((String) pObject).toUpperCase();
|
||||
}
|
||||
return pObject;
|
||||
}
|
||||
|
||||
protected Iterator<Entry<String, V>> newEntryIterator() {
|
||||
return (Iterator) entries.entrySet().iterator();
|
||||
}
|
||||
|
||||
protected Iterator<String> newKeyIterator() {
|
||||
return entries.keySet().iterator();
|
||||
}
|
||||
|
||||
protected Iterator<V> newValueIterator() {
|
||||
return (Iterator<V>) entries.values().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,468 +1,466 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Generic map and linked list implementation of the {@code Map} interface,
|
||||
* with predictable iteration order.
|
||||
* <p>
|
||||
* Resembles {@code LinkedHashMap} from JDK 1.4+, but is backed by a generic
|
||||
* {@code Map}, rather than implementing a particular algoritm.
|
||||
* <p>
|
||||
* This linked list defines the iteration ordering, which is normally the order
|
||||
* 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>
|
||||
* 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 true} immediately prior to the invocation).
|
||||
* <p>
|
||||
* 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
|
||||
* were last accessed, from least-recently accessed to most-recently
|
||||
* (<em>access-order</em>).
|
||||
* 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
|
||||
* corresponding entry (assuming it exists after the invocation completes).
|
||||
* 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
|
||||
* specified map's entry set iterator.
|
||||
* <em>No other methods generate entry accesses.</em>
|
||||
* In particular, operations on collection-views do not affect the order of
|
||||
* iteration of the backing map.
|
||||
* <p>
|
||||
* The {@link #removeEldestEntry(Map.Entry)} method may be overridden to impose
|
||||
* a policy for removing stale mappings automatically when new mappings are
|
||||
* added to the map.
|
||||
*
|
||||
* @author inspired by LinkedHashMap from JDK 1.4+, by Josh Bloch
|
||||
* @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 $
|
||||
*
|
||||
* @see LinkedHashMap
|
||||
* @see LRUMap
|
||||
*/
|
||||
public class LinkedMap<K, V> extends AbstractDecoratedMap<K, V> implements Serializable {
|
||||
|
||||
transient LinkedEntry<K, V> head;
|
||||
protected final boolean accessOrder;
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with default
|
||||
* (insertion) order.
|
||||
*/
|
||||
public LinkedMap() {
|
||||
this(null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with the
|
||||
* given order.
|
||||
*
|
||||
* @param pAccessOrder if {@code true}, ordering will be "least recently
|
||||
* accessed item" to "latest accessed item", otherwise "first inserted item"
|
||||
* to "latest inserted item".
|
||||
*/
|
||||
public LinkedMap(boolean pAccessOrder) {
|
||||
this(null, pAccessOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
|
||||
* pairs copied from {@code pContents} and default (insertion) order.
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
*/
|
||||
public LinkedMap(Map<? extends K, ? extends V> pContents) {
|
||||
this(pContents, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
|
||||
* pairs copied from {@code pContents} and the given order.
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @param pAccessOrder if {@code true}, ordering will be "least recently
|
||||
* accessed item" to "latest accessed item", otherwise "first inserted item"
|
||||
* to "latest inserted item".
|
||||
*/
|
||||
public LinkedMap(Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
|
||||
super(pContents);
|
||||
accessOrder = pAccessOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by the given map, with key/value
|
||||
* pairs copied from {@code pContents} and default (insertion) order.
|
||||
*
|
||||
* @param pBacking the backing map of this map. Must be either empty, or
|
||||
* the same map as {@code pContents}.
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
*/
|
||||
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
|
||||
this(pBacking, pContents, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by the given map, with key/value
|
||||
* pairs copied from {@code pContents} and the given order.
|
||||
*
|
||||
* @param pBacking the backing map of this map. Must be either empty, or
|
||||
* the same map as {@code pContents}.
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @param pAccessOrder if {@code true}, ordering will be "least recently
|
||||
* accessed item" to "latest accessed item", otherwise "first inserted item"
|
||||
* to "latest inserted item".
|
||||
*/
|
||||
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
|
||||
super(pBacking, pContents);
|
||||
accessOrder = pAccessOrder;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
head = new LinkedEntry<K, V>(null, null, null) {
|
||||
void addBefore(LinkedEntry pExisting) {
|
||||
throw new Error();
|
||||
}
|
||||
void remove() {
|
||||
throw new Error();
|
||||
}
|
||||
public void recordAccess(Map pMap) {
|
||||
throw new Error();
|
||||
}
|
||||
public void recordRemoval(Map pMap) {
|
||||
throw new Error();
|
||||
}
|
||||
public void recordRemoval() {
|
||||
throw new Error();
|
||||
}
|
||||
public V getValue() {
|
||||
throw new Error();
|
||||
}
|
||||
public V setValue(V pValue) {
|
||||
throw new Error();
|
||||
}
|
||||
public K getKey() {
|
||||
throw new Error();
|
||||
}
|
||||
public String toString() {
|
||||
return "head";
|
||||
}
|
||||
};
|
||||
head.previous = head.next = head;
|
||||
}
|
||||
|
||||
public boolean containsValue(Object pValue) {
|
||||
// Overridden to take advantage of faster iterator
|
||||
if (pValue == null) {
|
||||
for (LinkedEntry e = head.next; e != head; e = e.next) {
|
||||
if (e.mValue == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (LinkedEntry e = head.next; e != head; e = e.next) {
|
||||
if (pValue.equals(e.mValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Iterator<K> newKeyIterator() {
|
||||
return new KeyIterator();
|
||||
}
|
||||
|
||||
protected Iterator<V> newValueIterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
protected Iterator<Entry<K, V>> newEntryIterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
private abstract class LinkedMapIterator<E> implements Iterator<E> {
|
||||
LinkedEntry<K, V> mNextEntry = head.next;
|
||||
LinkedEntry<K, V> mLastReturned = null;
|
||||
|
||||
/**
|
||||
* The modCount value that the iterator believes that the backing
|
||||
* List should have. If this expectation is violated, the iterator
|
||||
* has detected concurrent modification.
|
||||
*/
|
||||
int mExpectedModCount = modCount;
|
||||
|
||||
public boolean hasNext() {
|
||||
return mNextEntry != head;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (mLastReturned == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
if (modCount != mExpectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
LinkedMap.this.remove(mLastReturned.mKey);
|
||||
mLastReturned = null;
|
||||
|
||||
mExpectedModCount = modCount;
|
||||
}
|
||||
|
||||
LinkedEntry<K, V> nextEntry() {
|
||||
if (modCount != mExpectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
if (mNextEntry == head) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
LinkedEntry<K, V> e = mLastReturned = mNextEntry;
|
||||
mNextEntry = e.next;
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyIterator extends LinkedMap<K, V>.LinkedMapIterator<K> {
|
||||
public K next() {
|
||||
return nextEntry().mKey;
|
||||
}
|
||||
}
|
||||
|
||||
private class ValueIterator extends LinkedMap<K, V>.LinkedMapIterator<V> {
|
||||
public V next() {
|
||||
return nextEntry().mValue;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryIterator extends LinkedMap<K, V>.LinkedMapIterator<Entry<K, V>> {
|
||||
public Entry<K, V> next() {
|
||||
return nextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
public V get(Object pKey) {
|
||||
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
|
||||
|
||||
if (entry != null) {
|
||||
entry.recordAccess(this);
|
||||
return entry.mValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public V remove(Object pKey) {
|
||||
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.remove(pKey);
|
||||
|
||||
if (entry != null) {
|
||||
entry.remove();
|
||||
modCount++;
|
||||
|
||||
return entry.mValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public V put(K pKey, V pValue) {
|
||||
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
|
||||
V oldValue;
|
||||
|
||||
if (entry == null) {
|
||||
oldValue = null;
|
||||
|
||||
// Remove eldest entry if instructed, else grow capacity if appropriate
|
||||
LinkedEntry<K, V> eldest = head.next;
|
||||
if (removeEldestEntry(eldest)) {
|
||||
removeEntry(eldest);
|
||||
}
|
||||
|
||||
entry = createEntry(pKey, pValue);
|
||||
entry.addBefore(head);
|
||||
|
||||
entries.put(pKey, entry);
|
||||
}
|
||||
else {
|
||||
oldValue = entry.mValue;
|
||||
|
||||
entry.mValue = pValue;
|
||||
entry.recordAccess(this);
|
||||
}
|
||||
|
||||
modCount++;
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code LinkedEntry}.
|
||||
*
|
||||
* @param pKey the key
|
||||
* @param pValue the value
|
||||
* @return a new LinkedEntry
|
||||
*/
|
||||
/*protected*/ LinkedEntry<K, V> createEntry(K pKey, V pValue) {
|
||||
return new LinkedEntry<K, V>(pKey, pValue, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*
|
||||
* @return a copy of this map, with the same order and same key/value pairs.
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
LinkedMap map;
|
||||
|
||||
map = (LinkedMap) super.clone();
|
||||
|
||||
// TODO: The rest of the work is PROBABLY handled by
|
||||
// AbstractDecoratedMap, but need to verify that.
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this map should remove its eldest entry.
|
||||
* This method is invoked by {@code put} and {@code putAll} after
|
||||
* inserting a new entry into the map. It provides the implementer
|
||||
* with the opportunity to remove the eldest entry each time a new one
|
||||
* 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
|
||||
* added, maintaining a steady state of 100 entries.
|
||||
* <pre>
|
||||
* private static final int MAX_ENTRIES = 100;
|
||||
*
|
||||
* protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
* return size() > MAX_ENTRIES;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This method typically does not modify the map in any way,
|
||||
* instead allowing the map to modify itself as directed by its
|
||||
* return value. It <i>is</i> permitted for this method to modify
|
||||
* the map directly, but if it does so, it <i>must</i> return
|
||||
* {@code false} (indicating that the map should not attempt any
|
||||
* 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).
|
||||
*
|
||||
* @param pEldest The least recently inserted entry in the map, or if
|
||||
* this is an access-ordered map, the least recently accessed
|
||||
* entry. This is the entry that will be removed it this
|
||||
* method returns {@code true}. If the map was empty prior
|
||||
* to the {@code put} or {@code putAll} invocation resulting
|
||||
* in this invocation, this will be the entry that was just
|
||||
* inserted; in other words, if the map contains a single
|
||||
* entry, the eldest entry is also the newest.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linked list implementation of {@code Map.Entry}.
|
||||
*/
|
||||
protected static class LinkedEntry<K, V> extends BasicEntry<K, V> implements Serializable {
|
||||
LinkedEntry<K, V> previous;
|
||||
LinkedEntry<K, V> next;
|
||||
|
||||
LinkedEntry(K pKey, V pValue, LinkedEntry<K, V> pNext) {
|
||||
super(pKey, pValue);
|
||||
|
||||
next = pNext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this entry before the given entry (which must be an existing
|
||||
* entry) in the list.
|
||||
*
|
||||
* @param pExisting the entry to add before
|
||||
*/
|
||||
void addBefore(LinkedEntry<K, V> pExisting) {
|
||||
next = pExisting;
|
||||
previous = pExisting.previous;
|
||||
|
||||
previous.next = this;
|
||||
next.previous = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this entry from the linked list.
|
||||
*/
|
||||
void remove() {
|
||||
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.
|
||||
*
|
||||
* @param pMap the map to record access for
|
||||
*/
|
||||
protected void recordAccess(Map<K, V> pMap) {
|
||||
LinkedMap<K, V> linkedMap = (LinkedMap<K, V>) pMap;
|
||||
if (linkedMap.accessOrder) {
|
||||
linkedMap.modCount++;
|
||||
remove();
|
||||
addBefore(linkedMap.head);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this entry from the linked list.
|
||||
*
|
||||
* @param pMap the map to record removal from
|
||||
*/
|
||||
protected void recordRemoval(Map<K, V> pMap) {
|
||||
// TODO: Is this REALLY correct?
|
||||
remove();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Generic map and linked list implementation of the {@code Map} interface,
|
||||
* with predictable iteration order.
|
||||
* <p>
|
||||
* Resembles {@code LinkedHashMap} from JDK 1.4+, but is backed by a generic
|
||||
* {@code Map}, rather than implementing a particular algoritm.
|
||||
* <p>
|
||||
* This linked list defines the iteration ordering, which is normally the order
|
||||
* 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>
|
||||
* 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 true} immediately prior to the invocation).
|
||||
* <p>
|
||||
* 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
|
||||
* were last accessed, from least-recently accessed to most-recently
|
||||
* (<em>access-order</em>).
|
||||
* 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
|
||||
* corresponding entry (assuming it exists after the invocation completes).
|
||||
* 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
|
||||
* specified map's entry set iterator.
|
||||
* <em>No other methods generate entry accesses.</em>
|
||||
* In particular, operations on collection-views do not affect the order of
|
||||
* iteration of the backing map.
|
||||
* <p>
|
||||
* The {@link #removeEldestEntry(Map.Entry)} method may be overridden to impose
|
||||
* a policy for removing stale mappings automatically when new mappings are
|
||||
* added to the map.
|
||||
*
|
||||
* @author inspired by LinkedHashMap from JDK 1.4+, by Josh Bloch
|
||||
* @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 $
|
||||
*
|
||||
* @see LinkedHashMap
|
||||
* @see LRUMap
|
||||
*/
|
||||
public class LinkedMap<K, V> extends AbstractDecoratedMap<K, V> implements Serializable {
|
||||
|
||||
transient LinkedEntry<K, V> head;
|
||||
protected final boolean accessOrder;
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with default
|
||||
* (insertion) order.
|
||||
*/
|
||||
public LinkedMap() {
|
||||
this(null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with the
|
||||
* given order.
|
||||
*
|
||||
* @param pAccessOrder if {@code true}, ordering will be "least recently
|
||||
* accessed item" to "latest accessed item", otherwise "first inserted item"
|
||||
* to "latest inserted item".
|
||||
*/
|
||||
public LinkedMap(boolean pAccessOrder) {
|
||||
this(null, pAccessOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
|
||||
* pairs copied from {@code pContents} and default (insertion) order.
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
*/
|
||||
public LinkedMap(Map<? extends K, ? extends V> pContents) {
|
||||
this(pContents, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
|
||||
* pairs copied from {@code pContents} and the given order.
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @param pAccessOrder if {@code true}, ordering will be "least recently
|
||||
* accessed item" to "latest accessed item", otherwise "first inserted item"
|
||||
* to "latest inserted item".
|
||||
*/
|
||||
public LinkedMap(Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
|
||||
super(pContents);
|
||||
accessOrder = pAccessOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by the given map, with key/value
|
||||
* pairs copied from {@code pContents} and default (insertion) order.
|
||||
*
|
||||
* @param pBacking the backing map of this map. Must be either empty, or
|
||||
* the same map as {@code pContents}.
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
*/
|
||||
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
|
||||
this(pBacking, pContents, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code LinkedMap} backed by the given map, with key/value
|
||||
* pairs copied from {@code pContents} and the given order.
|
||||
*
|
||||
* @param pBacking the backing map of this map. Must be either empty, or
|
||||
* the same map as {@code pContents}.
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @param pAccessOrder if {@code true}, ordering will be "least recently
|
||||
* accessed item" to "latest accessed item", otherwise "first inserted item"
|
||||
* to "latest inserted item".
|
||||
*/
|
||||
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
|
||||
super(pBacking, pContents);
|
||||
accessOrder = pAccessOrder;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
head = new LinkedEntry<K, V>(null, null, null) {
|
||||
void addBefore(LinkedEntry pExisting) {
|
||||
throw new Error();
|
||||
}
|
||||
void remove() {
|
||||
throw new Error();
|
||||
}
|
||||
public void recordAccess(Map pMap) {
|
||||
throw new Error();
|
||||
}
|
||||
public void recordRemoval(Map pMap) {
|
||||
throw new Error();
|
||||
}
|
||||
public void recordRemoval() {
|
||||
throw new Error();
|
||||
}
|
||||
public V getValue() {
|
||||
throw new Error();
|
||||
}
|
||||
public V setValue(V pValue) {
|
||||
throw new Error();
|
||||
}
|
||||
public K getKey() {
|
||||
throw new Error();
|
||||
}
|
||||
public String toString() {
|
||||
return "head";
|
||||
}
|
||||
};
|
||||
head.previous = head.next = head;
|
||||
}
|
||||
|
||||
public boolean containsValue(Object pValue) {
|
||||
// Overridden to take advantage of faster iterator
|
||||
if (pValue == null) {
|
||||
for (LinkedEntry e = head.next; e != head; e = e.next) {
|
||||
if (e.mValue == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (LinkedEntry e = head.next; e != head; e = e.next) {
|
||||
if (pValue.equals(e.mValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Iterator<K> newKeyIterator() {
|
||||
return new KeyIterator();
|
||||
}
|
||||
|
||||
protected Iterator<V> newValueIterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
protected Iterator<Entry<K, V>> newEntryIterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
private abstract class LinkedMapIterator<E> implements Iterator<E> {
|
||||
LinkedEntry<K, V> mNextEntry = head.next;
|
||||
LinkedEntry<K, V> mLastReturned = null;
|
||||
|
||||
/**
|
||||
* The modCount value that the iterator believes that the backing
|
||||
* List should have. If this expectation is violated, the iterator
|
||||
* has detected concurrent modification.
|
||||
*/
|
||||
int mExpectedModCount = modCount;
|
||||
|
||||
public boolean hasNext() {
|
||||
return mNextEntry != head;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (mLastReturned == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
if (modCount != mExpectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
LinkedMap.this.remove(mLastReturned.mKey);
|
||||
mLastReturned = null;
|
||||
|
||||
mExpectedModCount = modCount;
|
||||
}
|
||||
|
||||
LinkedEntry<K, V> nextEntry() {
|
||||
if (modCount != mExpectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
if (mNextEntry == head) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
LinkedEntry<K, V> e = mLastReturned = mNextEntry;
|
||||
mNextEntry = e.next;
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyIterator extends LinkedMap<K, V>.LinkedMapIterator<K> {
|
||||
public K next() {
|
||||
return nextEntry().mKey;
|
||||
}
|
||||
}
|
||||
|
||||
private class ValueIterator extends LinkedMap<K, V>.LinkedMapIterator<V> {
|
||||
public V next() {
|
||||
return nextEntry().mValue;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryIterator extends LinkedMap<K, V>.LinkedMapIterator<Entry<K, V>> {
|
||||
public Entry<K, V> next() {
|
||||
return nextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
public V get(Object pKey) {
|
||||
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
|
||||
|
||||
if (entry != null) {
|
||||
entry.recordAccess(this);
|
||||
return entry.mValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public V remove(Object pKey) {
|
||||
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.remove(pKey);
|
||||
|
||||
if (entry != null) {
|
||||
entry.remove();
|
||||
modCount++;
|
||||
|
||||
return entry.mValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public V put(K pKey, V pValue) {
|
||||
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
|
||||
V oldValue;
|
||||
|
||||
if (entry == null) {
|
||||
oldValue = null;
|
||||
|
||||
// Remove eldest entry if instructed, else grow capacity if appropriate
|
||||
LinkedEntry<K, V> eldest = head.next;
|
||||
if (removeEldestEntry(eldest)) {
|
||||
removeEntry(eldest);
|
||||
}
|
||||
|
||||
entry = createEntry(pKey, pValue);
|
||||
entry.addBefore(head);
|
||||
|
||||
entries.put(pKey, entry);
|
||||
}
|
||||
else {
|
||||
oldValue = entry.mValue;
|
||||
|
||||
entry.mValue = pValue;
|
||||
entry.recordAccess(this);
|
||||
}
|
||||
|
||||
modCount++;
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code LinkedEntry}.
|
||||
*
|
||||
* @param pKey the key
|
||||
* @param pValue the value
|
||||
* @return a new LinkedEntry
|
||||
*/
|
||||
/*protected*/ LinkedEntry<K, V> createEntry(K pKey, V pValue) {
|
||||
return new LinkedEntry<K, V>(pKey, pValue, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a copy of this map, with the same order and same key/value pairs.
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
LinkedMap map;
|
||||
|
||||
map = (LinkedMap) super.clone();
|
||||
|
||||
// TODO: The rest of the work is PROBABLY handled by
|
||||
// AbstractDecoratedMap, but need to verify that.
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this map should remove its eldest entry.
|
||||
* This method is invoked by {@code put} and {@code putAll} after
|
||||
* inserting a new entry into the map. It provides the implementer
|
||||
* with the opportunity to remove the eldest entry each time a new one
|
||||
* 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
|
||||
* added, maintaining a steady state of 100 entries.
|
||||
* <pre>
|
||||
* private static final int MAX_ENTRIES = 100;
|
||||
*
|
||||
* protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
* return size() > MAX_ENTRIES;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This method typically does not modify the map in any way,
|
||||
* instead allowing the map to modify itself as directed by its
|
||||
* return value. It <i>is</i> permitted for this method to modify
|
||||
* the map directly, but if it does so, it <i>must</i> return
|
||||
* {@code false} (indicating that the map should not attempt any
|
||||
* 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).
|
||||
*
|
||||
* @param pEldest The least recently inserted entry in the map, or if
|
||||
* this is an access-ordered map, the least recently accessed
|
||||
* entry. This is the entry that will be removed it this
|
||||
* method returns {@code true}. If the map was empty prior
|
||||
* to the {@code put} or {@code putAll} invocation resulting
|
||||
* in this invocation, this will be the entry that was just
|
||||
* inserted; in other words, if the map contains a single
|
||||
* entry, the eldest entry is also the newest.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linked list implementation of {@code Map.Entry}.
|
||||
*/
|
||||
protected static class LinkedEntry<K, V> extends BasicEntry<K, V> implements Serializable {
|
||||
LinkedEntry<K, V> previous;
|
||||
LinkedEntry<K, V> next;
|
||||
|
||||
LinkedEntry(K pKey, V pValue, LinkedEntry<K, V> pNext) {
|
||||
super(pKey, pValue);
|
||||
|
||||
next = pNext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this entry before the given entry (which must be an existing
|
||||
* entry) in the list.
|
||||
*
|
||||
* @param pExisting the entry to add before
|
||||
*/
|
||||
void addBefore(LinkedEntry<K, V> pExisting) {
|
||||
next = pExisting;
|
||||
previous = pExisting.previous;
|
||||
|
||||
previous.next = this;
|
||||
next.previous = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this entry from the linked list.
|
||||
*/
|
||||
void remove() {
|
||||
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.
|
||||
*
|
||||
* @param pMap the map to record access for
|
||||
*/
|
||||
protected void recordAccess(Map<K, V> pMap) {
|
||||
LinkedMap<K, V> linkedMap = (LinkedMap<K, V>) pMap;
|
||||
if (linkedMap.accessOrder) {
|
||||
linkedMap.modCount++;
|
||||
remove();
|
||||
addBefore(linkedMap.head);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this entry from the linked list.
|
||||
*
|
||||
* @param pMap the map to record removal from
|
||||
*/
|
||||
protected void recordRemoval(Map<K, V> pMap) {
|
||||
// TODO: Is this REALLY correct?
|
||||
remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Generic map and linked list implementation of the {@code Set} interface,
|
||||
* with predictable iteration order.
|
||||
* <p>
|
||||
* Resembles {@code LinkedHashSet} from JDK 1.4+, but is backed by a generic
|
||||
* {@code LinkedMap}, rather than implementing a particular algoritm.
|
||||
* <p/>
|
||||
* @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 $
|
||||
*/
|
||||
public class LinkedSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
|
||||
|
||||
private final static Object DUMMY = new Object();
|
||||
|
||||
private final Map<E, Object> map;
|
||||
|
||||
public LinkedSet() {
|
||||
map = new LinkedMap<E, Object>();
|
||||
}
|
||||
|
||||
public LinkedSet(Collection<E> pCollection) {
|
||||
this();
|
||||
addAll(pCollection);
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends E> pCollection) {
|
||||
boolean changed = false;
|
||||
for (E value : pCollection) {
|
||||
if (add(value) && !changed) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean add(E pValue) {
|
||||
return map.put(pValue, DUMMY) == null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public Iterator<E> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Generic map and linked list implementation of the {@code Set} interface,
|
||||
* with predictable iteration order.
|
||||
* <p>
|
||||
* Resembles {@code LinkedHashSet} from JDK 1.4+, but is backed by a generic
|
||||
* {@code LinkedMap}, rather than implementing a particular algoritm.
|
||||
* </p>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class LinkedSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
|
||||
|
||||
private final static Object DUMMY = new Object();
|
||||
|
||||
private final Map<E, Object> map;
|
||||
|
||||
public LinkedSet() {
|
||||
map = new LinkedMap<E, Object>();
|
||||
}
|
||||
|
||||
public LinkedSet(Collection<E> pCollection) {
|
||||
this();
|
||||
addAll(pCollection);
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends E> pCollection) {
|
||||
boolean changed = false;
|
||||
for (E value : pCollection) {
|
||||
if (add(value) && !changed) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean add(E pValue) {
|
||||
return map.put(pValue, DUMMY) == null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public Iterator<E> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,122 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An (immutable) empty {@link Map}, that supports all {@code Map} operations
|
||||
* without throwing exceptions (in contrast to {@link Collections#EMPTY_MAP}
|
||||
* that will throw exceptions on {@code put}/{@code remove}).
|
||||
* <p/>
|
||||
* NOTE: This is not a general purpose {@code Map} implementation,
|
||||
* as the {@code put} and {@code putAll} methods will not modify the map.
|
||||
* Instances of this class will always be an empty map.
|
||||
*
|
||||
* @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() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final void clear() {
|
||||
}
|
||||
|
||||
public final boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean containsKey(Object pKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean containsValue(Object pValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Collection<V> values() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public final void putAll(Map pMap) {
|
||||
}
|
||||
|
||||
public final Set<Entry<K, V>> entrySet() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public final Set<K> keySet() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public final V get(Object pKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final V remove(Object pKey) {
|
||||
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}).
|
||||
* 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},
|
||||
* otherwise {@code false}
|
||||
*/
|
||||
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
|
||||
* Java Collections Framework.
|
||||
*
|
||||
* @return {@code 0}, always
|
||||
*/
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An (immutable) empty {@link Map}, that supports all {@code Map} operations
|
||||
* without throwing exceptions (in contrast to {@link Collections#EMPTY_MAP}
|
||||
* that will throw exceptions on {@code put}/{@code remove}).
|
||||
* <p>
|
||||
* NOTE: This is not a general purpose {@code Map} implementation,
|
||||
* as the {@code put} and {@code putAll} methods will not modify the 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 $
|
||||
*/
|
||||
public final class NullMap<K, V> implements Map<K, V>, Serializable {
|
||||
public final int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final void clear() {
|
||||
}
|
||||
|
||||
public final boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean containsKey(Object pKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean containsValue(Object pValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Collection<V> values() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public final void putAll(Map pMap) {
|
||||
}
|
||||
|
||||
public final Set<Entry<K, V>> entrySet() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public final Set<K> keySet() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public final V get(Object pKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final V remove(Object pKey) {
|
||||
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}).
|
||||
* 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},
|
||||
* otherwise {@code false}
|
||||
*/
|
||||
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
|
||||
* Java Collections Framework.
|
||||
*
|
||||
* @return {@code 0}, always
|
||||
*/
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,183 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
/**
|
||||
* Utility class for storing times in a simple way. The internal time is stored
|
||||
* as an int, counting seconds.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @todo Milliseconds!
|
||||
*/
|
||||
public class Time {
|
||||
|
||||
private int time = -1;
|
||||
public final static int SECONDS_IN_MINUTE = 60;
|
||||
|
||||
/**
|
||||
* Creates a new time with 0 seconds, 0 minutes.
|
||||
*/
|
||||
public Time() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new time with the given time (in seconds).
|
||||
*/
|
||||
public Time(int pTime) {
|
||||
setTime(pTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the full time in seconds
|
||||
*/
|
||||
public void setTime(int pTime) {
|
||||
if (pTime < 0) {
|
||||
throw new IllegalArgumentException("Time argument must be 0 or positive!");
|
||||
}
|
||||
time = pTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full time in seconds.
|
||||
*/
|
||||
public int getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full time in milliseconds, for use in creating dates or
|
||||
* similar.
|
||||
*
|
||||
* @see java.util.Date#setTime(long)
|
||||
*/
|
||||
public long getTimeInMillis() {
|
||||
return (long) time * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param pSeconds an integer that should be between 0 and 59.
|
||||
*/
|
||||
public void setSeconds(int pSeconds) {
|
||||
time = getMinutes() * SECONDS_IN_MINUTE + pSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the seconds part of the time.
|
||||
*
|
||||
* @return an integer between 0 and 59
|
||||
*/
|
||||
public int getSeconds() {
|
||||
return time % SECONDS_IN_MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minutes part of the time.
|
||||
*
|
||||
* @param pMinutes an integer
|
||||
*/
|
||||
public void setMinutes(int pMinutes) {
|
||||
time = pMinutes * SECONDS_IN_MINUTE + getSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minutes part of the time.
|
||||
*
|
||||
* @return an integer
|
||||
*/
|
||||
public int getMinutes() {
|
||||
return time / SECONDS_IN_MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the time object.
|
||||
* The string is returned on the form m:ss,
|
||||
* where m is variable digits minutes and ss is two digits seconds.
|
||||
*
|
||||
* @return a string representation of the time object
|
||||
* @see #toString(String)
|
||||
*/
|
||||
public String toString() {
|
||||
return "" + getMinutes() + ":"
|
||||
+ (getSeconds() < 10 ? "0" : "") + getSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the time object.
|
||||
* The string returned is on the format of the formatstring.
|
||||
* <DL>
|
||||
* <DD>m (or any multiple of m's)
|
||||
* <DT>the minutes part (padded with 0's, if number has less digits than
|
||||
* the number of m's)
|
||||
* m -> 0,1,...,59,60,61,...
|
||||
* mm -> 00,01,...,59,60,61,...
|
||||
* <DD>s or ss
|
||||
* <DT>the seconds part (padded with 0's, if number has less digits than
|
||||
* the number of s's)
|
||||
* s -> 0,1,...,59
|
||||
* ss -> 00,01,...,59
|
||||
* <DD>S
|
||||
* <DT>all seconds (including the ones above 59)
|
||||
* </DL>
|
||||
*
|
||||
* @param pFormatStr the format where
|
||||
* @return a string representation of the time object
|
||||
* @throws NumberFormatException
|
||||
* @see TimeFormat#format(Time)
|
||||
* @see #parseTime(String)
|
||||
* @deprecated
|
||||
*/
|
||||
public String toString(String pFormatStr) {
|
||||
TimeFormat tf = new TimeFormat(pFormatStr);
|
||||
|
||||
return tf.format(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the time object.
|
||||
* The string is returned on the form m:ss,
|
||||
* where m is variable digits minutes and ss is two digits seconds.
|
||||
*
|
||||
* @return a string representation of the time object
|
||||
* @throws NumberFormatException
|
||||
* @see TimeFormat#parse(String)
|
||||
* @see #toString(String)
|
||||
* @deprecated
|
||||
*/
|
||||
public static Time parseTime(String pStr) {
|
||||
TimeFormat tf = TimeFormat.getInstance();
|
||||
|
||||
return tf.parse(pStr);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
/**
|
||||
* Utility class for storing times in a simple way. The internal time is stored
|
||||
* as an int, counting seconds.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
// TODO: Milliseconds!
|
||||
public class Time {
|
||||
|
||||
private int time = -1;
|
||||
public final static int SECONDS_IN_MINUTE = 60;
|
||||
|
||||
/**
|
||||
* Creates a new time with 0 seconds, 0 minutes.
|
||||
*/
|
||||
public Time() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new time with the given time (in seconds).
|
||||
*/
|
||||
public Time(int pTime) {
|
||||
setTime(pTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the full time in seconds
|
||||
*/
|
||||
public void setTime(int pTime) {
|
||||
if (pTime < 0) {
|
||||
throw new IllegalArgumentException("Time argument must be 0 or positive!");
|
||||
}
|
||||
time = pTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full time in seconds.
|
||||
*/
|
||||
public int getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full time in milliseconds, for use in creating dates or
|
||||
* similar.
|
||||
*
|
||||
* @see java.util.Date#setTime(long)
|
||||
*/
|
||||
public long getTimeInMillis() {
|
||||
return (long) time * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param pSeconds an integer that should be between 0 and 59.
|
||||
*/
|
||||
public void setSeconds(int pSeconds) {
|
||||
time = getMinutes() * SECONDS_IN_MINUTE + pSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the seconds part of the time.
|
||||
*
|
||||
* @return an integer between 0 and 59
|
||||
*/
|
||||
public int getSeconds() {
|
||||
return time % SECONDS_IN_MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minutes part of the time.
|
||||
*
|
||||
* @param pMinutes an integer
|
||||
*/
|
||||
public void setMinutes(int pMinutes) {
|
||||
time = pMinutes * SECONDS_IN_MINUTE + getSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minutes part of the time.
|
||||
*
|
||||
* @return an integer
|
||||
*/
|
||||
public int getMinutes() {
|
||||
return time / SECONDS_IN_MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the time object.
|
||||
* The string is returned on the form m:ss,
|
||||
* where m is variable digits minutes and ss is two digits seconds.
|
||||
*
|
||||
* @return a string representation of the time object
|
||||
* @see #toString(String)
|
||||
*/
|
||||
public String toString() {
|
||||
return "" + getMinutes() + ":"
|
||||
+ (getSeconds() < 10 ? "0" : "") + getSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the time object.
|
||||
* The string returned is on the format of the formatstring.
|
||||
* <DL>
|
||||
* <DD>m (or any multiple of m's)
|
||||
* <DT>the minutes part (padded with 0's, if number has less digits than
|
||||
* the number of m's)
|
||||
* m -> 0,1,...,59,60,61,...
|
||||
* mm -> 00,01,...,59,60,61,...
|
||||
* <DD>s or ss
|
||||
* <DT>the seconds part (padded with 0's, if number has less digits than
|
||||
* the number of s's)
|
||||
* s -> 0,1,...,59
|
||||
* ss -> 00,01,...,59
|
||||
* <DD>S
|
||||
* <DT>all seconds (including the ones above 59)
|
||||
* </DL>
|
||||
*
|
||||
* @param pFormatStr the format where
|
||||
* @return a string representation of the time object
|
||||
* @throws NumberFormatException
|
||||
* @see TimeFormat#format(Time)
|
||||
* @see #parseTime(String)
|
||||
* @deprecated
|
||||
*/
|
||||
public String toString(String pFormatStr) {
|
||||
TimeFormat tf = new TimeFormat(pFormatStr);
|
||||
|
||||
return tf.format(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the time object.
|
||||
* The string is returned on the form m:ss,
|
||||
* where m is variable digits minutes and ss is two digits seconds.
|
||||
*
|
||||
* @return a string representation of the time object
|
||||
* @throws NumberFormatException
|
||||
* @see TimeFormat#parse(String)
|
||||
* @see #toString(String)
|
||||
* @deprecated
|
||||
*/
|
||||
public static Time parseTime(String pStr) {
|
||||
TimeFormat tf = TimeFormat.getInstance();
|
||||
|
||||
return tf.parse(pStr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,452 +1,452 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import java.text.FieldPosition;
|
||||
import java.text.Format;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Format for converting and parsing time.
|
||||
* <P>
|
||||
* The format is expressed in a string as follows:
|
||||
* <DL>
|
||||
* <DD>m (or any multiple of m's)
|
||||
* <DT>the minutes part (padded with 0's, if number has less digits than
|
||||
* the number of m's)
|
||||
* m -> 0,1,...,59,60,61,...
|
||||
* mm -> 00,01,...,59,60,61,...
|
||||
* <DD>s or ss
|
||||
* <DT>the seconds part (padded with 0's, if number has less digits than
|
||||
* the number of s's)
|
||||
* s -> 0,1,...,59
|
||||
* ss -> 00,01,...,59
|
||||
* <DD>S
|
||||
* <DT>all seconds (including the ones above 59)
|
||||
* </DL>
|
||||
* <P>
|
||||
* May not handle all cases, and formats... ;-)
|
||||
* Safest is: Always delimiters between the minutes (m) and seconds (s) part.
|
||||
* <P>
|
||||
* TODO:
|
||||
* Move to com.twelvemonkeys.text?
|
||||
* Milliseconds!
|
||||
* Fix bugs.
|
||||
* Known bugs:
|
||||
* <P>
|
||||
* The last character in the formatString is not escaped, while it should be.
|
||||
* The first character after an escaped character is escaped while is shouldn't
|
||||
* be.
|
||||
* <P>
|
||||
* This is not a 100% compatible implementation of a java.text.Format.
|
||||
*
|
||||
* @see com.twelvemonkeys.util.Time
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public class TimeFormat extends Format {
|
||||
final static String MINUTE = "m";
|
||||
final static String SECOND = "s";
|
||||
final static String TIME = "S";
|
||||
final static String ESCAPE = "\\";
|
||||
|
||||
/**
|
||||
* The default time format
|
||||
*/
|
||||
|
||||
private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss");
|
||||
protected String formatString = null;
|
||||
|
||||
/**
|
||||
* Main method for testing ONLY
|
||||
*/
|
||||
|
||||
static void main(String[] argv) {
|
||||
Time time = null;
|
||||
TimeFormat in = null;
|
||||
TimeFormat out = null;
|
||||
|
||||
if (argv.length >= 3) {
|
||||
System.out.println("Creating out TimeFormat: \"" + argv[2] + "\"");
|
||||
out = new TimeFormat(argv[2]);
|
||||
}
|
||||
|
||||
if (argv.length >= 2) {
|
||||
System.out.println("Creating in TimeFormat: \"" + argv[1] + "\"");
|
||||
in = new TimeFormat(argv[1]);
|
||||
}
|
||||
else {
|
||||
System.out.println("Using default format for in");
|
||||
in = DEFAULT_FORMAT;
|
||||
}
|
||||
|
||||
if (out == null)
|
||||
out = in;
|
||||
|
||||
if (argv.length >= 1) {
|
||||
System.out.println("Parsing: \"" + argv[0] + "\" with format \""
|
||||
+ in.formatString + "\"");
|
||||
time = in.parse(argv[0]);
|
||||
}
|
||||
else
|
||||
time = new Time();
|
||||
|
||||
System.out.println("Time is \"" + out.format(time) +
|
||||
"\" according to format \"" + out.formatString + "\"");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The formatter array.
|
||||
*/
|
||||
|
||||
protected TimeFormatter[] formatter;
|
||||
|
||||
/**
|
||||
* Creates a new TimeFormat with the given formatString,
|
||||
*/
|
||||
|
||||
public TimeFormat(String pStr) {
|
||||
formatString = pStr;
|
||||
|
||||
Vector formatter = new Vector();
|
||||
StringTokenizer tok = new StringTokenizer(pStr, "\\msS", true);
|
||||
|
||||
String previous = null;
|
||||
String current = null;
|
||||
int previousCount = 0;
|
||||
|
||||
while (tok.hasMoreElements()) {
|
||||
current = tok.nextToken();
|
||||
|
||||
if (previous != null && previous.equals(ESCAPE)) {
|
||||
// Handle escaping of s, S or m
|
||||
current = ((current != null) ? current : "")
|
||||
+ (tok.hasMoreElements() ? tok.nextToken() : "");
|
||||
previous = null;
|
||||
previousCount = 0;
|
||||
}
|
||||
|
||||
// Skip over first,
|
||||
// or if current is the same, increase count, and try again
|
||||
if (previous == null || previous.equals(current)) {
|
||||
previousCount++;
|
||||
previous = current;
|
||||
}
|
||||
else {
|
||||
// Create new formatter for each part
|
||||
if (previous.equals(MINUTE))
|
||||
formatter.add(new MinutesFormatter(previousCount));
|
||||
else if (previous.equals(SECOND))
|
||||
formatter.add(new SecondsFormatter(previousCount));
|
||||
else if (previous.equals(TIME))
|
||||
formatter.add(new SecondsFormatter(-1));
|
||||
else
|
||||
formatter.add(new TextFormatter(previous));
|
||||
|
||||
previousCount = 1;
|
||||
previous = current;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Add new formatter for last part
|
||||
if (previous != null) {
|
||||
if (previous.equals(MINUTE))
|
||||
formatter.add(new MinutesFormatter(previousCount));
|
||||
else if (previous.equals(SECOND))
|
||||
formatter.add(new SecondsFormatter(previousCount));
|
||||
else if (previous.equals(TIME))
|
||||
formatter.add(new SecondsFormatter(-1));
|
||||
else
|
||||
formatter.add(new TextFormatter(previous));
|
||||
}
|
||||
|
||||
// Debug
|
||||
/*
|
||||
for (int i = 0; i < formatter.size(); i++) {
|
||||
System.out.println("Formatter " + formatter.get(i).getClass()
|
||||
+ ": length=" + ((TimeFormatter) formatter.get(i)).digits);
|
||||
}
|
||||
*/
|
||||
this.formatter = (TimeFormatter[])
|
||||
formatter.toArray(new TimeFormatter[formatter.size()]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* DUMMY IMPLEMENTATION!!
|
||||
* Not locale specific.
|
||||
*/
|
||||
|
||||
public static TimeFormat getInstance() {
|
||||
return DEFAULT_FORMAT;
|
||||
}
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
/* Not locale specific
|
||||
public static TimeFormat getInstance(Locale pLocale) {
|
||||
return DEFAULT_FORMAT;
|
||||
}
|
||||
*/
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
/* Not locale specific
|
||||
public static Locale[] getAvailableLocales() {
|
||||
return new Locale[] {Locale.getDefault()};
|
||||
}
|
||||
*/
|
||||
|
||||
/** Gets the format string. */
|
||||
public String getFormatString() {
|
||||
return formatString;
|
||||
}
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
public StringBuffer format(Object pObj, StringBuffer pToAppendTo,
|
||||
FieldPosition pPos) {
|
||||
if (!(pObj instanceof Time)) {
|
||||
throw new IllegalArgumentException("Must be instance of " + Time.class);
|
||||
}
|
||||
|
||||
return pToAppendTo.append(format(pObj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the the given time, using this format.
|
||||
*/
|
||||
|
||||
public String format(Time pTime) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < formatter.length; i++) {
|
||||
buf.append(formatter[i].format(pTime));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
public Object parseObject(String pStr, ParsePosition pStatus) {
|
||||
Time t = parse(pStr);
|
||||
|
||||
pStatus.setIndex(pStr.length()); // Not 100%
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a Time, according to this format.
|
||||
* <p>
|
||||
* Will bug on some formats. It's safest to always use delimiters between
|
||||
* the minutes (m) and seconds (s) part.
|
||||
*
|
||||
*/
|
||||
public Time parse(String pStr) {
|
||||
Time time = new Time();
|
||||
|
||||
int sec = 0;
|
||||
int min = 0;
|
||||
int pos = 0;
|
||||
int skip = 0;
|
||||
|
||||
boolean onlyUseSeconds = false;
|
||||
|
||||
for (int i = 0; (i < formatter.length)
|
||||
&& (pos + skip < pStr.length()) ; i++) {
|
||||
// Go to next offset
|
||||
pos += skip;
|
||||
|
||||
if (formatter[i] instanceof MinutesFormatter) {
|
||||
// Parse MINUTES
|
||||
if ((i + 1) < formatter.length
|
||||
&& formatter[i + 1] instanceof TextFormatter) {
|
||||
// Skip until next format element
|
||||
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
|
||||
// Error in format, try parsing to end
|
||||
if (skip < 0)
|
||||
skip = pStr.length();
|
||||
}
|
||||
else if ((i + 1) >= formatter.length) {
|
||||
// Skip until end of string
|
||||
skip = pStr.length();
|
||||
}
|
||||
else {
|
||||
// Hope this is correct...
|
||||
skip = formatter[i].digits;
|
||||
}
|
||||
|
||||
// May be first char
|
||||
if (skip > pos)
|
||||
min = Integer.parseInt(pStr.substring(pos, skip));
|
||||
}
|
||||
else if (formatter[i] instanceof SecondsFormatter) {
|
||||
// Parse SECONDS
|
||||
if (formatter[i].digits == -1) {
|
||||
// Only seconds (or full TIME)
|
||||
if ((i + 1) < formatter.length
|
||||
&& formatter[i + 1] instanceof TextFormatter) {
|
||||
// Skip until next format element
|
||||
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
|
||||
|
||||
}
|
||||
else if ((i + 1) >= formatter.length) {
|
||||
// Skip until end of string
|
||||
skip = pStr.length();
|
||||
}
|
||||
else {
|
||||
// Cannot possibly know how long?
|
||||
skip = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get seconds
|
||||
sec = Integer.parseInt(pStr.substring(pos, skip));
|
||||
// System.out.println("Only seconds: " + sec);
|
||||
|
||||
onlyUseSeconds = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Normal SECONDS
|
||||
if ((i + 1) < formatter.length
|
||||
&& formatter[i + 1] instanceof TextFormatter) {
|
||||
// Skip until next format element
|
||||
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
|
||||
|
||||
}
|
||||
else if ((i + 1) >= formatter.length) {
|
||||
// Skip until end of string
|
||||
skip = pStr.length();
|
||||
}
|
||||
else {
|
||||
skip = formatter[i].digits;
|
||||
}
|
||||
// Get seconds
|
||||
sec = Integer.parseInt(pStr.substring(pos, skip));
|
||||
}
|
||||
}
|
||||
else if (formatter[i] instanceof TextFormatter) {
|
||||
skip = formatter[i].digits;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set the minutes part if we should
|
||||
if (!onlyUseSeconds)
|
||||
time.setMinutes(min);
|
||||
|
||||
// Set the seconds part
|
||||
time.setSeconds(sec);
|
||||
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The base class of TimeFormatters
|
||||
*/
|
||||
abstract class TimeFormatter {
|
||||
int digits = 0;
|
||||
|
||||
abstract String format(Time t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the seconds part of the Time
|
||||
*/
|
||||
class SecondsFormatter extends TimeFormatter {
|
||||
|
||||
SecondsFormatter(int pDigits) {
|
||||
digits = pDigits;
|
||||
}
|
||||
|
||||
String format(Time t) {
|
||||
// Negative number of digits, means all seconds, no padding
|
||||
if (digits < 0) {
|
||||
return Integer.toString(t.getTime());
|
||||
}
|
||||
|
||||
// If seconds is more than digits long, simply return it
|
||||
if (t.getSeconds() >= Math.pow(10, digits)) {
|
||||
return Integer.toString(t.getSeconds());
|
||||
}
|
||||
|
||||
// Else return it with leading 0's
|
||||
//return StringUtil.formatNumber(t.getSeconds(), digits);
|
||||
return StringUtil.pad("" + t.getSeconds(), digits, "0", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the minutes part of the Time
|
||||
*/
|
||||
class MinutesFormatter extends TimeFormatter {
|
||||
|
||||
MinutesFormatter(int pDigits) {
|
||||
digits = pDigits;
|
||||
}
|
||||
|
||||
String format(Time t) {
|
||||
// If minutes is more than digits long, simply return it
|
||||
if (t.getMinutes() >= Math.pow(10, digits)) {
|
||||
return Integer.toString(t.getMinutes());
|
||||
}
|
||||
|
||||
// Else return it with leading 0's
|
||||
//return StringUtil.formatNumber(t.getMinutes(), digits);
|
||||
return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats text constant part of the Time
|
||||
*/
|
||||
class TextFormatter extends TimeFormatter {
|
||||
String text = null;
|
||||
|
||||
TextFormatter(String pText) {
|
||||
text = pText;
|
||||
|
||||
// Just to be able to skip over
|
||||
if (pText != null) {
|
||||
digits = pText.length();
|
||||
}
|
||||
}
|
||||
|
||||
String format(Time t) {
|
||||
// Simply return the text
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import java.text.FieldPosition;
|
||||
import java.text.Format;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Format for converting and parsing time.
|
||||
* <P>
|
||||
* The format is expressed in a string as follows:
|
||||
* <DL>
|
||||
* <DD>m (or any multiple of m's)
|
||||
* <DT>the minutes part (padded with 0's, if number has less digits than
|
||||
* the number of m's)
|
||||
* m -> 0,1,...,59,60,61,...
|
||||
* mm -> 00,01,...,59,60,61,...
|
||||
* <DD>s or ss
|
||||
* <DT>the seconds part (padded with 0's, if number has less digits than
|
||||
* the number of s's)
|
||||
* s -> 0,1,...,59
|
||||
* ss -> 00,01,...,59
|
||||
* <DD>S
|
||||
* <DT>all seconds (including the ones above 59)
|
||||
* </DL>
|
||||
* <P>
|
||||
* May not handle all cases, and formats... ;-)
|
||||
* Safest is: Always delimiters between the minutes (m) and seconds (s) part.
|
||||
* <P>
|
||||
* Known bugs:
|
||||
* <P>
|
||||
* The last character in the formatString is not escaped, while it should be.
|
||||
* The first character after an escaped character is escaped while is shouldn't
|
||||
* be.
|
||||
* <P>
|
||||
* This is not a 100% compatible implementation of a java.text.Format.
|
||||
*
|
||||
* @see com.twelvemonkeys.util.Time
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
// TODO:
|
||||
// Move to com.twelvemonkeys.text?
|
||||
// Milliseconds!
|
||||
// Fix bugs.
|
||||
public class TimeFormat extends Format {
|
||||
final static String MINUTE = "m";
|
||||
final static String SECOND = "s";
|
||||
final static String TIME = "S";
|
||||
final static String ESCAPE = "\\";
|
||||
|
||||
/**
|
||||
* The default time format
|
||||
*/
|
||||
|
||||
private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss");
|
||||
protected String formatString = null;
|
||||
|
||||
/**
|
||||
* Main method for testing ONLY
|
||||
*/
|
||||
|
||||
static void main(String[] argv) {
|
||||
Time time = null;
|
||||
TimeFormat in = null;
|
||||
TimeFormat out = null;
|
||||
|
||||
if (argv.length >= 3) {
|
||||
System.out.println("Creating out TimeFormat: \"" + argv[2] + "\"");
|
||||
out = new TimeFormat(argv[2]);
|
||||
}
|
||||
|
||||
if (argv.length >= 2) {
|
||||
System.out.println("Creating in TimeFormat: \"" + argv[1] + "\"");
|
||||
in = new TimeFormat(argv[1]);
|
||||
}
|
||||
else {
|
||||
System.out.println("Using default format for in");
|
||||
in = DEFAULT_FORMAT;
|
||||
}
|
||||
|
||||
if (out == null)
|
||||
out = in;
|
||||
|
||||
if (argv.length >= 1) {
|
||||
System.out.println("Parsing: \"" + argv[0] + "\" with format \""
|
||||
+ in.formatString + "\"");
|
||||
time = in.parse(argv[0]);
|
||||
}
|
||||
else
|
||||
time = new Time();
|
||||
|
||||
System.out.println("Time is \"" + out.format(time) +
|
||||
"\" according to format \"" + out.formatString + "\"");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The formatter array.
|
||||
*/
|
||||
|
||||
protected TimeFormatter[] formatter;
|
||||
|
||||
/**
|
||||
* Creates a new TimeFormat with the given formatString,
|
||||
*/
|
||||
|
||||
public TimeFormat(String pStr) {
|
||||
formatString = pStr;
|
||||
|
||||
Vector formatter = new Vector();
|
||||
StringTokenizer tok = new StringTokenizer(pStr, "\\msS", true);
|
||||
|
||||
String previous = null;
|
||||
String current = null;
|
||||
int previousCount = 0;
|
||||
|
||||
while (tok.hasMoreElements()) {
|
||||
current = tok.nextToken();
|
||||
|
||||
if (previous != null && previous.equals(ESCAPE)) {
|
||||
// Handle escaping of s, S or m
|
||||
current = ((current != null) ? current : "")
|
||||
+ (tok.hasMoreElements() ? tok.nextToken() : "");
|
||||
previous = null;
|
||||
previousCount = 0;
|
||||
}
|
||||
|
||||
// Skip over first,
|
||||
// or if current is the same, increase count, and try again
|
||||
if (previous == null || previous.equals(current)) {
|
||||
previousCount++;
|
||||
previous = current;
|
||||
}
|
||||
else {
|
||||
// Create new formatter for each part
|
||||
if (previous.equals(MINUTE))
|
||||
formatter.add(new MinutesFormatter(previousCount));
|
||||
else if (previous.equals(SECOND))
|
||||
formatter.add(new SecondsFormatter(previousCount));
|
||||
else if (previous.equals(TIME))
|
||||
formatter.add(new SecondsFormatter(-1));
|
||||
else
|
||||
formatter.add(new TextFormatter(previous));
|
||||
|
||||
previousCount = 1;
|
||||
previous = current;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Add new formatter for last part
|
||||
if (previous != null) {
|
||||
if (previous.equals(MINUTE))
|
||||
formatter.add(new MinutesFormatter(previousCount));
|
||||
else if (previous.equals(SECOND))
|
||||
formatter.add(new SecondsFormatter(previousCount));
|
||||
else if (previous.equals(TIME))
|
||||
formatter.add(new SecondsFormatter(-1));
|
||||
else
|
||||
formatter.add(new TextFormatter(previous));
|
||||
}
|
||||
|
||||
// Debug
|
||||
/*
|
||||
for (int i = 0; i < formatter.size(); i++) {
|
||||
System.out.println("Formatter " + formatter.get(i).getClass()
|
||||
+ ": length=" + ((TimeFormatter) formatter.get(i)).digits);
|
||||
}
|
||||
*/
|
||||
this.formatter = (TimeFormatter[])
|
||||
formatter.toArray(new TimeFormatter[formatter.size()]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* DUMMY IMPLEMENTATION!!
|
||||
* Not locale specific.
|
||||
*/
|
||||
|
||||
public static TimeFormat getInstance() {
|
||||
return DEFAULT_FORMAT;
|
||||
}
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
/* Not locale specific
|
||||
public static TimeFormat getInstance(Locale pLocale) {
|
||||
return DEFAULT_FORMAT;
|
||||
}
|
||||
*/
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
/* Not locale specific
|
||||
public static Locale[] getAvailableLocales() {
|
||||
return new Locale[] {Locale.getDefault()};
|
||||
}
|
||||
*/
|
||||
|
||||
/** Gets the format string. */
|
||||
public String getFormatString() {
|
||||
return formatString;
|
||||
}
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
public StringBuffer format(Object pObj, StringBuffer pToAppendTo,
|
||||
FieldPosition pPos) {
|
||||
if (!(pObj instanceof Time)) {
|
||||
throw new IllegalArgumentException("Must be instance of " + Time.class);
|
||||
}
|
||||
|
||||
return pToAppendTo.append(format(pObj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the the given time, using this format.
|
||||
*/
|
||||
|
||||
public String format(Time pTime) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < formatter.length; i++) {
|
||||
buf.append(formatter[i].format(pTime));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** DUMMY IMPLEMENTATION!! */
|
||||
public Object parseObject(String pStr, ParsePosition pStatus) {
|
||||
Time t = parse(pStr);
|
||||
|
||||
pStatus.setIndex(pStr.length()); // Not 100%
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a Time, according to this format.
|
||||
* <p>
|
||||
* Will bug on some formats. It's safest to always use delimiters between
|
||||
* the minutes (m) and seconds (s) part.
|
||||
*
|
||||
*/
|
||||
public Time parse(String pStr) {
|
||||
Time time = new Time();
|
||||
|
||||
int sec = 0;
|
||||
int min = 0;
|
||||
int pos = 0;
|
||||
int skip = 0;
|
||||
|
||||
boolean onlyUseSeconds = false;
|
||||
|
||||
for (int i = 0; (i < formatter.length)
|
||||
&& (pos + skip < pStr.length()) ; i++) {
|
||||
// Go to next offset
|
||||
pos += skip;
|
||||
|
||||
if (formatter[i] instanceof MinutesFormatter) {
|
||||
// Parse MINUTES
|
||||
if ((i + 1) < formatter.length
|
||||
&& formatter[i + 1] instanceof TextFormatter) {
|
||||
// Skip until next format element
|
||||
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
|
||||
// Error in format, try parsing to end
|
||||
if (skip < 0)
|
||||
skip = pStr.length();
|
||||
}
|
||||
else if ((i + 1) >= formatter.length) {
|
||||
// Skip until end of string
|
||||
skip = pStr.length();
|
||||
}
|
||||
else {
|
||||
// Hope this is correct...
|
||||
skip = formatter[i].digits;
|
||||
}
|
||||
|
||||
// May be first char
|
||||
if (skip > pos)
|
||||
min = Integer.parseInt(pStr.substring(pos, skip));
|
||||
}
|
||||
else if (formatter[i] instanceof SecondsFormatter) {
|
||||
// Parse SECONDS
|
||||
if (formatter[i].digits == -1) {
|
||||
// Only seconds (or full TIME)
|
||||
if ((i + 1) < formatter.length
|
||||
&& formatter[i + 1] instanceof TextFormatter) {
|
||||
// Skip until next format element
|
||||
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
|
||||
|
||||
}
|
||||
else if ((i + 1) >= formatter.length) {
|
||||
// Skip until end of string
|
||||
skip = pStr.length();
|
||||
}
|
||||
else {
|
||||
// Cannot possibly know how long?
|
||||
skip = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get seconds
|
||||
sec = Integer.parseInt(pStr.substring(pos, skip));
|
||||
// System.out.println("Only seconds: " + sec);
|
||||
|
||||
onlyUseSeconds = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Normal SECONDS
|
||||
if ((i + 1) < formatter.length
|
||||
&& formatter[i + 1] instanceof TextFormatter) {
|
||||
// Skip until next format element
|
||||
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
|
||||
|
||||
}
|
||||
else if ((i + 1) >= formatter.length) {
|
||||
// Skip until end of string
|
||||
skip = pStr.length();
|
||||
}
|
||||
else {
|
||||
skip = formatter[i].digits;
|
||||
}
|
||||
// Get seconds
|
||||
sec = Integer.parseInt(pStr.substring(pos, skip));
|
||||
}
|
||||
}
|
||||
else if (formatter[i] instanceof TextFormatter) {
|
||||
skip = formatter[i].digits;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set the minutes part if we should
|
||||
if (!onlyUseSeconds)
|
||||
time.setMinutes(min);
|
||||
|
||||
// Set the seconds part
|
||||
time.setSeconds(sec);
|
||||
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The base class of TimeFormatters
|
||||
*/
|
||||
abstract class TimeFormatter {
|
||||
int digits = 0;
|
||||
|
||||
abstract String format(Time t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the seconds part of the Time
|
||||
*/
|
||||
class SecondsFormatter extends TimeFormatter {
|
||||
|
||||
SecondsFormatter(int pDigits) {
|
||||
digits = pDigits;
|
||||
}
|
||||
|
||||
String format(Time t) {
|
||||
// Negative number of digits, means all seconds, no padding
|
||||
if (digits < 0) {
|
||||
return Integer.toString(t.getTime());
|
||||
}
|
||||
|
||||
// If seconds is more than digits long, simply return it
|
||||
if (t.getSeconds() >= Math.pow(10, digits)) {
|
||||
return Integer.toString(t.getSeconds());
|
||||
}
|
||||
|
||||
// Else return it with leading 0's
|
||||
//return StringUtil.formatNumber(t.getSeconds(), digits);
|
||||
return StringUtil.pad("" + t.getSeconds(), digits, "0", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the minutes part of the Time
|
||||
*/
|
||||
class MinutesFormatter extends TimeFormatter {
|
||||
|
||||
MinutesFormatter(int pDigits) {
|
||||
digits = pDigits;
|
||||
}
|
||||
|
||||
String format(Time t) {
|
||||
// If minutes is more than digits long, simply return it
|
||||
if (t.getMinutes() >= Math.pow(10, digits)) {
|
||||
return Integer.toString(t.getMinutes());
|
||||
}
|
||||
|
||||
// Else return it with leading 0's
|
||||
//return StringUtil.formatNumber(t.getMinutes(), digits);
|
||||
return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats text constant part of the Time
|
||||
*/
|
||||
class TextFormatter extends TimeFormatter {
|
||||
String text = null;
|
||||
|
||||
TextFormatter(String pText) {
|
||||
text = pText;
|
||||
|
||||
// Just to be able to skip over
|
||||
if (pText != null) {
|
||||
digits = pText.length();
|
||||
}
|
||||
}
|
||||
|
||||
String format(Time t) {
|
||||
// Simply return the text
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,451 +1,453 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 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},
|
||||
* or can be instantiated with any given {@code Map} as backing.
|
||||
* <P/>
|
||||
* Notes to consider when using this map:
|
||||
* <ul>
|
||||
* <li>Elements may not expire on the exact millisecond as expected.</li>
|
||||
* <li>The value returned by the {@code size()} method of the map, or any of
|
||||
* its collection views, may not represent
|
||||
* the exact number of entries in the map at any given time.</li>
|
||||
* <li>Elements in this map may expire at any time
|
||||
* (but never between invocations of {@code Iterator.hasNext()}
|
||||
* and {@code Iterator.next()} or {@code Iterator.remove()},
|
||||
* 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 $
|
||||
*
|
||||
* @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
|
||||
* that should not expire, as elements are in insertion order.
|
||||
* and next expiry time would be the time of the first element.
|
||||
* @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 {
|
||||
/**
|
||||
* Expiry time
|
||||
*/
|
||||
protected long expiryTime = 60000L; // 1 minute
|
||||
|
||||
//////////////////////
|
||||
private volatile long nextExpiryTime;
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
* Creates a {@code TimeoutMap} with the default expiry time of 1 minute.
|
||||
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
|
||||
* <p/>
|
||||
* <small>This is constructor is here to comply with the reccomendations for
|
||||
* "standard" constructors in the {@code Map} interface.</small>
|
||||
*
|
||||
* @see #TimeoutMap(long)
|
||||
*/
|
||||
public TimeoutMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code TimeoutMap} containing the same elements as the given map
|
||||
* with the default expiry time of 1 minute.
|
||||
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance,
|
||||
* and <em>not</em> the map passed in as a paramter.
|
||||
* <p/>
|
||||
* <small>This is constructor is here to comply with the reccomendations for
|
||||
* "standard" constructors in the {@code Map} interface.</small>
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @see #TimeoutMap(java.util.Map, Map, long)
|
||||
* @see java.util.Map
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pExpiryTime the expiry time (time to live) for elements in this map
|
||||
*/
|
||||
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/>
|
||||
* <EM>Note that structurally modifying the backing map directly (not
|
||||
* through this map or its collection views), is not allowed, and will
|
||||
* produce undeterministic exceptions.</EM>
|
||||
*
|
||||
* @param pBacking the map that will be used as backing.
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @param pExpiryTime the expiry time (time to live) for elements in this map
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public void setExpiryTime(long pExpiryTime) {
|
||||
long oldEexpiryTime = expiryTime;
|
||||
|
||||
expiryTime = pExpiryTime;
|
||||
|
||||
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}.
|
||||
*
|
||||
* @return the number of key-value mappings in this map.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
* @return {@code true} if this map contains a mapping for the specified
|
||||
* 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
|
||||
* explicitly maps the pKey to {@code null}. The {@code containsKey}
|
||||
* operation may be used to distinguish these two cases.
|
||||
*
|
||||
* @param pKey pKey whose associated value is to be returned.
|
||||
* @return the value to which this map maps the specified pKey, or
|
||||
* {@code null} if the map contains no mapping for this pKey.
|
||||
* @see #containsKey(java.lang.Object)
|
||||
*/
|
||||
public V get(Object pKey) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
else if (entry.isExpired()) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
entries.remove(pKey);
|
||||
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.
|
||||
*
|
||||
* @param pKey pKey with which the specified pValue is to be associated.
|
||||
* @param pValue pValue to be associated with the specified pKey.
|
||||
* @return previous pValue associated with specified pKey, or {@code null}
|
||||
* if there was no mapping for pKey. A {@code null} return can
|
||||
* also indicate that the map previously associated {@code null}
|
||||
* with the specified pKey, if the implementation supports
|
||||
* {@code null} values.
|
||||
*/
|
||||
public V put(K pKey, V pValue) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
V oldValue;
|
||||
|
||||
if (entry == null) {
|
||||
oldValue = null;
|
||||
|
||||
entry = createEntry(pKey, pValue);
|
||||
|
||||
entries.put(pKey, entry);
|
||||
}
|
||||
else {
|
||||
oldValue = entry.mValue;
|
||||
entry.setValue(pValue);
|
||||
entry.recordAccess(this);
|
||||
}
|
||||
|
||||
// Need to remove expired objects every now and then
|
||||
// We do it in the put method, to avoid resource leaks over time.
|
||||
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.
|
||||
* @return previous value associated with specified pKey, or {@code null}
|
||||
* if there was no mapping for pKey. A {@code null} return can
|
||||
* also indicate that the map previously associated {@code null}
|
||||
* with the specified pKey, if the implementation supports
|
||||
* {@code null} values.
|
||||
*/
|
||||
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.. :-)
|
||||
init();
|
||||
}
|
||||
|
||||
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
|
||||
return new TimedEntry<K, V>(pKey, pValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any expired mappings.
|
||||
*
|
||||
*/
|
||||
protected void removeExpiredEntries() {
|
||||
// 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.
|
||||
*
|
||||
* @param pTime now
|
||||
*/
|
||||
private synchronized void removeExpiredEntriesSynced(long pTime) {
|
||||
if (pTime > nextExpiryTime) {
|
||||
////
|
||||
long next = Long.MAX_VALUE;
|
||||
nextExpiryTime = next; // Avoid multiple runs...
|
||||
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
|
||||
////
|
||||
long expires = entry.expires();
|
||||
if (expires < next) {
|
||||
next = expires;
|
||||
}
|
||||
////
|
||||
}
|
||||
////
|
||||
nextExpiryTime = next;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<V> values() {
|
||||
removeExpiredEntries();
|
||||
return super.values();
|
||||
}
|
||||
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
removeExpiredEntries();
|
||||
return super.entrySet();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
protected Iterator<V> newValueIterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
protected Iterator<Entry<K, V>> newEntryIterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
public void processRemoved(Entry pRemoved) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Iterating through this iterator will remove any expired values.
|
||||
*/
|
||||
private abstract class TimeoutMapIterator<E> implements Iterator<E> {
|
||||
Iterator<Entry<K, Entry<K, V>>> mIterator = entries.entrySet().iterator();
|
||||
BasicEntry<K, V> mNext;
|
||||
long mNow = System.currentTimeMillis();
|
||||
|
||||
public void remove() {
|
||||
mNext = null; // advance
|
||||
mIterator.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();
|
||||
|
||||
if (timed.isExpiredBy(mNow)) {
|
||||
// Remove from map, and continue
|
||||
mIterator.remove();
|
||||
processRemoved(timed);
|
||||
}
|
||||
else {
|
||||
// Go with this entry
|
||||
mNext = timed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BasicEntry<K, V> nextEntry() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
BasicEntry<K, V> entry = mNext;
|
||||
mNext = null; // advance
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
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 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> {
|
||||
private long mTimestamp;
|
||||
|
||||
TimedEntry(K pKey, V pValue) {
|
||||
super(pKey, pValue);
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public V setValue(V pValue) {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
return super.setValue(pValue);
|
||||
}
|
||||
|
||||
final boolean isExpired() {
|
||||
return isExpiredBy(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
final boolean isExpiredBy(final long pTime) {
|
||||
return pTime > expires();
|
||||
}
|
||||
|
||||
final long expires() {
|
||||
return mTimestamp + expiryTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 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},
|
||||
* or can be instantiated with any given {@code Map} as backing.
|
||||
* <p>
|
||||
* Notes to consider when using this map:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Elements may not expire on the exact millisecond as expected.</li>
|
||||
* <li>The value returned by the {@code size()} method of the map, or any of
|
||||
* its collection views, may not represent
|
||||
* the exact number of entries in the map at any given time.</li>
|
||||
* <li>Elements in this map may expire at any time
|
||||
* (but never between invocations of {@code Iterator.hasNext()}
|
||||
* and {@code Iterator.next()} or {@code Iterator.remove()},
|
||||
* 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 $
|
||||
*/
|
||||
// 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
|
||||
// that should not expire, as elements are in insertion order.
|
||||
// and next expiry time would be the time of the first element.
|
||||
// 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 {
|
||||
/**
|
||||
* Expiry time
|
||||
*/
|
||||
protected long expiryTime = 60000L; // 1 minute
|
||||
|
||||
//////////////////////
|
||||
private volatile long nextExpiryTime;
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
* Creates a {@code TimeoutMap} with the default expiry time of 1 minute.
|
||||
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
|
||||
* <p>
|
||||
* <small>This is constructor is here to comply with the recommendations for
|
||||
* "standard" constructors in the {@code Map} interface.</small>
|
||||
* </p>
|
||||
*
|
||||
* @see #TimeoutMap(long)
|
||||
*/
|
||||
public TimeoutMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code TimeoutMap} containing the same elements as the given map
|
||||
* with the default expiry time of 1 minute.
|
||||
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance,
|
||||
* and <em>not</em> the map passed in as a paramter.
|
||||
* <p>
|
||||
* <small>This is constructor is here to comply with the recommendations for
|
||||
* "standard" constructors in the {@code Map} interface.</small>
|
||||
* </p>
|
||||
*
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @see #TimeoutMap(java.util.Map, Map, long)
|
||||
* @see java.util.Map
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pExpiryTime the expiry time (time to live) for elements in this map
|
||||
*/
|
||||
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>
|
||||
* <em>Note that structurally modifying the backing map directly (not
|
||||
* through this map or its collection views), is not allowed, and will
|
||||
* produce undeterministic exceptions.</em>
|
||||
* </p>
|
||||
*
|
||||
* @param pBacking the map that will be used as backing.
|
||||
* @param pContents the map whose mappings are to be placed in this map.
|
||||
* May be {@code null}.
|
||||
* @param pExpiryTime the expiry time (time to live) for elements in this map
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public void setExpiryTime(long pExpiryTime) {
|
||||
long oldEexpiryTime = expiryTime;
|
||||
|
||||
expiryTime = pExpiryTime;
|
||||
|
||||
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}.
|
||||
*
|
||||
* @return the number of key-value mappings in this map.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
* @return {@code true} if this map contains a mapping for the specified
|
||||
* 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
|
||||
* explicitly maps the pKey to {@code null}. The {@code containsKey}
|
||||
* operation may be used to distinguish these two cases.
|
||||
*
|
||||
* @param pKey pKey whose associated value is to be returned.
|
||||
* @return the value to which this map maps the specified pKey, or
|
||||
* {@code null} if the map contains no mapping for this pKey.
|
||||
* @see #containsKey(java.lang.Object)
|
||||
*/
|
||||
public V get(Object pKey) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
else if (entry.isExpired()) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
entries.remove(pKey);
|
||||
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.
|
||||
*
|
||||
* @param pKey pKey with which the specified pValue is to be associated.
|
||||
* @param pValue pValue to be associated with the specified pKey.
|
||||
* @return previous pValue associated with specified pKey, or {@code null}
|
||||
* if there was no mapping for pKey. A {@code null} return can
|
||||
* also indicate that the map previously associated {@code null}
|
||||
* with the specified pKey, if the implementation supports
|
||||
* {@code null} values.
|
||||
*/
|
||||
public V put(K pKey, V pValue) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
V oldValue;
|
||||
|
||||
if (entry == null) {
|
||||
oldValue = null;
|
||||
|
||||
entry = createEntry(pKey, pValue);
|
||||
|
||||
entries.put(pKey, entry);
|
||||
}
|
||||
else {
|
||||
oldValue = entry.mValue;
|
||||
entry.setValue(pValue);
|
||||
entry.recordAccess(this);
|
||||
}
|
||||
|
||||
// Need to remove expired objects every now and then
|
||||
// We do it in the put method, to avoid resource leaks over time.
|
||||
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.
|
||||
* @return previous value associated with specified pKey, or {@code null}
|
||||
* if there was no mapping for pKey. A {@code null} return can
|
||||
* also indicate that the map previously associated {@code null}
|
||||
* with the specified pKey, if the implementation supports
|
||||
* {@code null} values.
|
||||
*/
|
||||
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.. :-)
|
||||
init();
|
||||
}
|
||||
|
||||
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
|
||||
return new TimedEntry<K, V>(pKey, pValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any expired mappings.
|
||||
*
|
||||
*/
|
||||
protected void removeExpiredEntries() {
|
||||
// Remove any expired elements
|
||||
long now = System.currentTimeMillis();
|
||||
if (now > nextExpiryTime) {
|
||||
removeExpiredEntriesSynced(now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Okay, I guess this do resemble DCL...
|
||||
*
|
||||
* @param pTime now
|
||||
*/
|
||||
// TODO: Write some exhausting multi-threaded unit-tests.
|
||||
private synchronized void removeExpiredEntriesSynced(long pTime) {
|
||||
if (pTime > nextExpiryTime) {
|
||||
////
|
||||
long next = Long.MAX_VALUE;
|
||||
nextExpiryTime = next; // Avoid multiple runs...
|
||||
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
|
||||
////
|
||||
long expires = entry.expires();
|
||||
if (expires < next) {
|
||||
next = expires;
|
||||
}
|
||||
////
|
||||
}
|
||||
////
|
||||
nextExpiryTime = next;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<V> values() {
|
||||
removeExpiredEntries();
|
||||
return super.values();
|
||||
}
|
||||
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
removeExpiredEntries();
|
||||
return super.entrySet();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
protected Iterator<V> newValueIterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
protected Iterator<Entry<K, V>> newEntryIterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
public void processRemoved(Entry pRemoved) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Iterating through this iterator will remove any expired values.
|
||||
*/
|
||||
private abstract class TimeoutMapIterator<E> implements Iterator<E> {
|
||||
Iterator<Entry<K, Entry<K, V>>> mIterator = entries.entrySet().iterator();
|
||||
BasicEntry<K, V> mNext;
|
||||
long mNow = System.currentTimeMillis();
|
||||
|
||||
public void remove() {
|
||||
mNext = null; // advance
|
||||
mIterator.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();
|
||||
|
||||
if (timed.isExpiredBy(mNow)) {
|
||||
// Remove from map, and continue
|
||||
mIterator.remove();
|
||||
processRemoved(timed);
|
||||
}
|
||||
else {
|
||||
// Go with this entry
|
||||
mNext = timed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BasicEntry<K, V> nextEntry() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
BasicEntry<K, V> entry = mNext;
|
||||
mNext = null; // advance
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
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 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> {
|
||||
private long mTimestamp;
|
||||
|
||||
TimedEntry(K pKey, V pValue) {
|
||||
super(pKey, pValue);
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public V setValue(V pValue) {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
return super.setValue(pValue);
|
||||
}
|
||||
|
||||
final boolean isExpired() {
|
||||
return isExpiredBy(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
final boolean isExpiredBy(final long pTime) {
|
||||
return pTime > expires();
|
||||
}
|
||||
|
||||
final long expires() {
|
||||
return mTimestamp + expiryTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* TokenIterator, Iterator-based replacement for StringTokenizer.
|
||||
* <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/util/TokenIterator.java#1 $
|
||||
*/
|
||||
public interface TokenIterator extends Iterator<String>, Enumeration<String> {
|
||||
boolean hasMoreTokens();
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration as a {@code String}.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @exception java.util.NoSuchElementException iteration has no more elements.
|
||||
*/
|
||||
String nextToken();
|
||||
|
||||
/**
|
||||
* Resets this iterator.
|
||||
*
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* TokenIterator, Iterator-based replacement for StringTokenizer.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface TokenIterator extends Iterator<String>, Enumeration<String> {
|
||||
boolean hasMoreTokens();
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration as a {@code String}.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @exception java.util.NoSuchElementException iteration has no more elements.
|
||||
*/
|
||||
String nextToken();
|
||||
|
||||
/**
|
||||
* Resets this iterator.
|
||||
*
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
|
||||
@@ -1,196 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.util.Time;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The converter (singleton). Converts strings to objects and back.
|
||||
* This is the entry point to the converter framework.
|
||||
* <p/>
|
||||
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
|
||||
* and {@link Object}
|
||||
* (the {@link DefaultConverter}) are registered by this class' static
|
||||
* initializer. You might remove them using the
|
||||
* {@code unregisterConverter} method.
|
||||
*
|
||||
* @see #registerConverter(Class, PropertyConverter)
|
||||
* @see #unregisterConverter(Class)
|
||||
*
|
||||
* @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/util/convert/Converter.java#1 $
|
||||
*/
|
||||
// TODO: Get rid of singleton stuff
|
||||
// Can probably be a pure static class, but is that a good idea?
|
||||
// Maybe have BeanUtil act as a "proxy", and hide this class all together?
|
||||
// TODO: ServiceRegistry for registering 3rd party converters
|
||||
// TODO: URI scheme, for implicit typing? Is that a good idea?
|
||||
// TODO: Array converters?
|
||||
public abstract class Converter implements PropertyConverter {
|
||||
|
||||
/** 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>();
|
||||
|
||||
// Register our predefined converters
|
||||
static {
|
||||
PropertyConverter defaultConverter = new DefaultConverter();
|
||||
registerConverter(Object.class, defaultConverter);
|
||||
registerConverter(Boolean.TYPE, defaultConverter);
|
||||
|
||||
PropertyConverter numberConverter = new NumberConverter();
|
||||
registerConverter(Number.class, numberConverter);
|
||||
registerConverter(Byte.TYPE, numberConverter);
|
||||
registerConverter(Double.TYPE, numberConverter);
|
||||
registerConverter(Float.TYPE, numberConverter);
|
||||
registerConverter(Integer.TYPE, numberConverter);
|
||||
registerConverter(Long.TYPE, numberConverter);
|
||||
registerConverter(Short.TYPE, numberConverter);
|
||||
|
||||
registerConverter(Date.class, new DateConverter());
|
||||
registerConverter(Time.class, new TimeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Converter.
|
||||
*/
|
||||
protected Converter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Converter instance.
|
||||
*
|
||||
* @return the converter instance
|
||||
*/
|
||||
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
|
||||
* specific version is registered.
|
||||
* </p>
|
||||
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
|
||||
* and {@link Object}
|
||||
* (the {@link DefaultConverter}) are registered by this class' static
|
||||
* initializer. You might remove them using the
|
||||
* {@code unregisterConverter} method.
|
||||
*
|
||||
* @param pType the (super) type to register a converter for
|
||||
* @param pConverter the converter
|
||||
*
|
||||
* @see #unregisterConverter(Class)
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pType the (super) type to remove converter for
|
||||
*
|
||||
* @see #registerConverter(Class,PropertyConverter)
|
||||
*/
|
||||
@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
|
||||
* @param pType the type to convert to
|
||||
*
|
||||
* @return the object created from the given string.
|
||||
*
|
||||
* @throws ConversionException if the string cannot be converted for any
|
||||
* reason.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pString the string to convert
|
||||
* @param pType the type to convert to
|
||||
* @param pFormat the (optional) conversion format
|
||||
*
|
||||
* @return the object created from the given string.
|
||||
*
|
||||
* @throws ConversionException if the string cannot be converted for any
|
||||
* reason.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return the string representation of the object, on the correct format.
|
||||
*
|
||||
* @throws ConversionException if the object cannot be converted to a
|
||||
* string for any reason.
|
||||
*/
|
||||
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.
|
||||
* @param pFormat the (optional) conversion format
|
||||
*
|
||||
* @return the string representation of the object, on the correct format.
|
||||
*
|
||||
* @throws ConversionException if the object cannot be converted to a
|
||||
* string for any reason.
|
||||
*/
|
||||
public abstract String toString(Object pObject, String pFormat)
|
||||
throws ConversionException;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.util.Time;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The converter (singleton). Converts strings to objects and back.
|
||||
* This is the entry point to the converter framework.
|
||||
* <p>
|
||||
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
|
||||
* and {@link Object}
|
||||
* (the {@link DefaultConverter}) are registered by this class' static
|
||||
* initializer. You might remove them using the
|
||||
* {@code unregisterConverter} method.
|
||||
* </p>
|
||||
*
|
||||
* @see #registerConverter(Class, PropertyConverter)
|
||||
* @see #unregisterConverter(Class)
|
||||
*
|
||||
* @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/util/convert/Converter.java#1 $
|
||||
*/
|
||||
// TODO: Get rid of singleton stuff
|
||||
// Can probably be a pure static class, but is that a good idea?
|
||||
// Maybe have BeanUtil act as a "proxy", and hide this class all together?
|
||||
// TODO: ServiceRegistry for registering 3rd party converters
|
||||
// TODO: URI scheme, for implicit typing? Is that a good idea?
|
||||
// TODO: Array converters?
|
||||
public abstract class Converter implements PropertyConverter {
|
||||
|
||||
/** 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>();
|
||||
|
||||
// Register our predefined converters
|
||||
static {
|
||||
PropertyConverter defaultConverter = new DefaultConverter();
|
||||
registerConverter(Object.class, defaultConverter);
|
||||
registerConverter(Boolean.TYPE, defaultConverter);
|
||||
|
||||
PropertyConverter numberConverter = new NumberConverter();
|
||||
registerConverter(Number.class, numberConverter);
|
||||
registerConverter(Byte.TYPE, numberConverter);
|
||||
registerConverter(Double.TYPE, numberConverter);
|
||||
registerConverter(Float.TYPE, numberConverter);
|
||||
registerConverter(Integer.TYPE, numberConverter);
|
||||
registerConverter(Long.TYPE, numberConverter);
|
||||
registerConverter(Short.TYPE, numberConverter);
|
||||
|
||||
registerConverter(Date.class, new DateConverter());
|
||||
registerConverter(Time.class, new TimeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Converter.
|
||||
*/
|
||||
protected Converter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Converter instance.
|
||||
*
|
||||
* @return the converter instance
|
||||
*/
|
||||
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
|
||||
* specific version is registered.
|
||||
* <p>
|
||||
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
|
||||
* and {@link Object}
|
||||
* (the {@link DefaultConverter}) are registered by this class' static
|
||||
* initializer. You might remove them using the
|
||||
* {@code unregisterConverter} method.
|
||||
* </p>
|
||||
*
|
||||
* @param pType the (super) type to register a converter for
|
||||
* @param pConverter the converter
|
||||
*
|
||||
* @see #unregisterConverter(Class)
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pType the (super) type to remove converter for
|
||||
*
|
||||
* @see #registerConverter(Class,PropertyConverter)
|
||||
*/
|
||||
@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
|
||||
* @param pType the type to convert to
|
||||
*
|
||||
* @return the object created from the given string.
|
||||
*
|
||||
* @throws ConversionException if the string cannot be converted for any
|
||||
* reason.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param pString the string to convert
|
||||
* @param pType the type to convert to
|
||||
* @param pFormat the (optional) conversion format
|
||||
*
|
||||
* @return the object created from the given string.
|
||||
*
|
||||
* @throws ConversionException if the string cannot be converted for any
|
||||
* reason.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return the string representation of the object, on the correct format.
|
||||
*
|
||||
* @throws ConversionException if the object cannot be converted to a
|
||||
* string for any reason.
|
||||
*/
|
||||
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.
|
||||
* @param pFormat the (optional) conversion format
|
||||
*
|
||||
* @return the string representation of the object, on the correct format.
|
||||
*
|
||||
* @throws ConversionException if the object cannot be converted to a
|
||||
* string for any reason.
|
||||
*/
|
||||
public abstract String toString(Object pObject, String pFormat)
|
||||
throws ConversionException;
|
||||
}
|
||||
|
||||
@@ -1,157 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.BeanUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Converts strings to dates and back.
|
||||
* <p/>
|
||||
* <small>This class has a static cache of {@code DateFormats}, to avoid
|
||||
* creation and parsing of date formats every time one is used.</small>
|
||||
*
|
||||
* @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/util/convert/DateConverter.java#2 $
|
||||
*/
|
||||
public class DateConverter extends NumberConverter {
|
||||
|
||||
/** Creates a {@code DateConverter} */
|
||||
public DateConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* subclasses allowed.
|
||||
* @param pFormat the format used for parsing. Must be a legal
|
||||
* {@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}
|
||||
*
|
||||
* @see Date
|
||||
* @see java.text.DateFormat
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public Object toObject(String pString, Class pType, String pFormat) throws ConversionException {
|
||||
if (StringUtil.isEmpty(pString))
|
||||
return null;
|
||||
|
||||
try {
|
||||
DateFormat format;
|
||||
|
||||
if (pFormat == null) {
|
||||
// Use system default format, using default locale
|
||||
format = DateFormat.getDateTimeInstance();
|
||||
}
|
||||
else {
|
||||
// Get format from cache
|
||||
format = getDateFormat(pFormat);
|
||||
}
|
||||
|
||||
Date date = StringUtil.toDate(pString, format);
|
||||
|
||||
// Allow for conversion to Date subclasses (ie. java.sql.*)
|
||||
if (pType != Date.class) {
|
||||
try {
|
||||
date = (Date) BeanUtil.createInstance(pType, new Long(date.getTime()));
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
throw new TypeMismathException(pType);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new ConversionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code SimpleDateFormat} format, or {@code null} which will use the
|
||||
* default 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}
|
||||
*
|
||||
* @see Date
|
||||
* @see java.text.DateFormat
|
||||
*/
|
||||
public String toString(Object pObject, String pFormat) throws ConversionException {
|
||||
if (pObject == null)
|
||||
return null;
|
||||
|
||||
if (!(pObject instanceof Date)) {
|
||||
throw new TypeMismathException(pObject.getClass());
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert to string, default way
|
||||
if (StringUtil.isEmpty(pFormat)) {
|
||||
return DateFormat.getDateTimeInstance().format(pObject);
|
||||
}
|
||||
|
||||
// Convert to string, using format
|
||||
DateFormat format = getDateFormat(pFormat);
|
||||
|
||||
return format.format(pObject);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
private DateFormat getDateFormat(String pFormat) {
|
||||
return (DateFormat) getFormat(SimpleDateFormat.class, pFormat, Locale.US);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.BeanUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Converts strings to dates and back.
|
||||
* <p>
|
||||
* <small>This class has a static cache of {@code DateFormats}, to avoid
|
||||
* 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 $
|
||||
* @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 {
|
||||
|
||||
/** Creates a {@code DateConverter} */
|
||||
public DateConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* subclasses allowed.
|
||||
* @param pFormat the format used for parsing. Must be a legal
|
||||
* {@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}
|
||||
*
|
||||
* @see Date
|
||||
* @see java.text.DateFormat
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public Object toObject(String pString, Class pType, String pFormat) throws ConversionException {
|
||||
if (StringUtil.isEmpty(pString))
|
||||
return null;
|
||||
|
||||
try {
|
||||
DateFormat format;
|
||||
|
||||
if (pFormat == null) {
|
||||
// Use system default format, using default locale
|
||||
format = DateFormat.getDateTimeInstance();
|
||||
}
|
||||
else {
|
||||
// Get format from cache
|
||||
format = getDateFormat(pFormat);
|
||||
}
|
||||
|
||||
Date date = StringUtil.toDate(pString, format);
|
||||
|
||||
// Allow for conversion to Date subclasses (ie. java.sql.*)
|
||||
if (pType != Date.class) {
|
||||
try {
|
||||
date = (Date) BeanUtil.createInstance(pType, new Long(date.getTime()));
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
throw new TypeMismathException(pType);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new ConversionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code SimpleDateFormat} format, or {@code null} which will use the
|
||||
* default 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}
|
||||
*
|
||||
* @see Date
|
||||
* @see java.text.DateFormat
|
||||
*/
|
||||
public String toString(Object pObject, String pFormat) throws ConversionException {
|
||||
if (pObject == null)
|
||||
return null;
|
||||
|
||||
if (!(pObject instanceof Date)) {
|
||||
throw new TypeMismathException(pObject.getClass());
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert to string, default way
|
||||
if (StringUtil.isEmpty(pFormat)) {
|
||||
return DateFormat.getDateTimeInstance().format(pObject);
|
||||
}
|
||||
|
||||
// Convert to string, using format
|
||||
DateFormat format = getDateFormat(pFormat);
|
||||
|
||||
return format.format(pObject);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
private DateFormat getDateFormat(String pFormat) {
|
||||
return (DateFormat) getFormat(SimpleDateFormat.class, pFormat, Locale.US);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,268 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.BeanUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Converts strings to objects and back.
|
||||
* <p/>
|
||||
* This converter first tries to create an object, using the class' single
|
||||
* string argument constructor ({@code <type>(String)}) if found,
|
||||
* otherwise, an attempt to call
|
||||
* the class' static {@code valueOf(String)} method. If both fails, a
|
||||
* {@link ConversionException} is thrown.
|
||||
*
|
||||
* @author <A href="haraldk@iconmedialab.no">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/util/convert/DefaultConverter.java#2 $
|
||||
*
|
||||
*/
|
||||
public final class DefaultConverter implements PropertyConverter {
|
||||
|
||||
/**
|
||||
* Creates a {@code DefaultConverter}.
|
||||
*/
|
||||
public DefaultConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the string to an object of the given type.
|
||||
*
|
||||
* @param pString the string to convert
|
||||
* @param pType the type to convert to
|
||||
* @param pFormat ignored.
|
||||
*
|
||||
* @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
|
||||
* {@code valueOf} method.
|
||||
*/
|
||||
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
|
||||
if (pString == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pType == null) {
|
||||
throw new MissingTypeException();
|
||||
}
|
||||
|
||||
if (pType.isArray()) {
|
||||
return toArray(pString, pType, pFormat);
|
||||
}
|
||||
|
||||
// TODO: Separate CollectionConverter?
|
||||
// should however, be simple to wrap array using Arrays.asList
|
||||
// But what about generic type?! It's erased...
|
||||
|
||||
// Primitive -> wrapper
|
||||
Class type = unBoxType(pType);
|
||||
|
||||
try {
|
||||
// Try to create instance from <Constructor>(String)
|
||||
Object value = BeanUtil.createInstance(type, pString);
|
||||
|
||||
if (value == null) {
|
||||
// createInstance failed for some reason
|
||||
// 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...
|
||||
throw new ConversionException(String.format(
|
||||
"Could not convert String to %1$s: No constructor %1$s(String) or static %1$s.valueOf(String) method found!",
|
||||
type.getName()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
catch (InvocationTargetException ite) {
|
||||
throw new ConversionException(ite.getTargetException());
|
||||
}
|
||||
catch (ConversionException ce) {
|
||||
throw ce;
|
||||
}
|
||||
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);
|
||||
Class type = pType.getComponentType();
|
||||
if (type == String.class) {
|
||||
return strings;
|
||||
}
|
||||
|
||||
Object array = Array.newInstance(type, strings.length);
|
||||
try {
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
Array.set(array, i, Converter.getInstance().toObject(strings[i], type));
|
||||
}
|
||||
}
|
||||
catch (ConversionException 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);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string, using {@code pObject.toString()}.
|
||||
*
|
||||
* @param pObject the object to convert.
|
||||
* @param pFormat ignored.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
try {
|
||||
return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString();
|
||||
}
|
||||
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 Object[] toObjectArray(final Object pObject) {
|
||||
// TODO: Extract util method for wrapping/unwrapping native arrays?
|
||||
Object[] array;
|
||||
Class<?> componentType = pObject.getClass().getComponentType();
|
||||
if (componentType.isPrimitive()) {
|
||||
if (int.class == componentType) {
|
||||
array = new Integer[Array.getLength(pObject)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
Array.set(array, i, Array.get(pObject, i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown type " + componentType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
array = (Object[]) pObject;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private Class<?> unBoxType(final Class<?> pType) {
|
||||
if (pType.isPrimitive()) {
|
||||
if (pType == boolean.class) {
|
||||
return Boolean.class;
|
||||
}
|
||||
if (pType == byte.class) {
|
||||
return Byte.class;
|
||||
}
|
||||
if (pType == char.class) {
|
||||
return Character.class;
|
||||
}
|
||||
if (pType == short.class) {
|
||||
return Short.class;
|
||||
}
|
||||
if (pType == int.class) {
|
||||
return Integer.class;
|
||||
}
|
||||
if (pType == float.class) {
|
||||
return Float.class;
|
||||
}
|
||||
if (pType == long.class) {
|
||||
return Long.class;
|
||||
}
|
||||
if (pType == double.class) {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown type: " + pType);
|
||||
}
|
||||
|
||||
return pType;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.BeanUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Converts strings to objects and back.
|
||||
* <p>
|
||||
* This converter first tries to create an object, using the class' single
|
||||
* string argument constructor ({@code <type>(String)}) if found,
|
||||
* otherwise, an attempt to call
|
||||
* the class' static {@code valueOf(String)} method. If both fails, a
|
||||
* {@link ConversionException} is thrown.
|
||||
* </p>
|
||||
*
|
||||
* @author <A href="haraldk@iconmedialab.no">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/util/convert/DefaultConverter.java#2 $
|
||||
*
|
||||
*/
|
||||
public final class DefaultConverter implements PropertyConverter {
|
||||
|
||||
/**
|
||||
* Creates a {@code DefaultConverter}.
|
||||
*/
|
||||
public DefaultConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the string to an object of the given type.
|
||||
*
|
||||
* @param pString the string to convert
|
||||
* @param pType the type to convert to
|
||||
* @param pFormat ignored.
|
||||
*
|
||||
* @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
|
||||
* {@code valueOf} method.
|
||||
*/
|
||||
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
|
||||
if (pString == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pType == null) {
|
||||
throw new MissingTypeException();
|
||||
}
|
||||
|
||||
if (pType.isArray()) {
|
||||
return toArray(pString, pType, pFormat);
|
||||
}
|
||||
|
||||
// TODO: Separate CollectionConverter?
|
||||
// should however, be simple to wrap array using Arrays.asList
|
||||
// But what about generic type?! It's erased...
|
||||
|
||||
// Primitive -> wrapper
|
||||
Class type = unBoxType(pType);
|
||||
|
||||
try {
|
||||
// Try to create instance from <Constructor>(String)
|
||||
Object value = BeanUtil.createInstance(type, pString);
|
||||
|
||||
if (value == null) {
|
||||
// createInstance failed for some reason
|
||||
// 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...
|
||||
throw new ConversionException(String.format(
|
||||
"Could not convert String to %1$s: No constructor %1$s(String) or static %1$s.valueOf(String) method found!",
|
||||
type.getName()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
catch (InvocationTargetException ite) {
|
||||
throw new ConversionException(ite.getTargetException());
|
||||
}
|
||||
catch (ConversionException ce) {
|
||||
throw ce;
|
||||
}
|
||||
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);
|
||||
Class type = pType.getComponentType();
|
||||
if (type == String.class) {
|
||||
return strings;
|
||||
}
|
||||
|
||||
Object array = Array.newInstance(type, strings.length);
|
||||
try {
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
Array.set(array, i, Converter.getInstance().toObject(strings[i], type));
|
||||
}
|
||||
}
|
||||
catch (ConversionException 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);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string, using {@code pObject.toString()}.
|
||||
*
|
||||
* @param pObject the object to convert.
|
||||
* @param pFormat ignored.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
try {
|
||||
return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString();
|
||||
}
|
||||
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 Object[] toObjectArray(final Object pObject) {
|
||||
// TODO: Extract util method for wrapping/unwrapping native arrays?
|
||||
Object[] array;
|
||||
Class<?> componentType = pObject.getClass().getComponentType();
|
||||
if (componentType.isPrimitive()) {
|
||||
if (int.class == componentType) {
|
||||
array = new Integer[Array.getLength(pObject)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
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)];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
Array.set(array, i, Array.get(pObject, i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown type " + componentType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
array = (Object[]) pObject;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private Class<?> unBoxType(final Class<?> pType) {
|
||||
if (pType.isPrimitive()) {
|
||||
if (pType == boolean.class) {
|
||||
return Boolean.class;
|
||||
}
|
||||
if (pType == byte.class) {
|
||||
return Byte.class;
|
||||
}
|
||||
if (pType == char.class) {
|
||||
return Character.class;
|
||||
}
|
||||
if (pType == short.class) {
|
||||
return Short.class;
|
||||
}
|
||||
if (pType == int.class) {
|
||||
return Integer.class;
|
||||
}
|
||||
if (pType == float.class) {
|
||||
return Float.class;
|
||||
}
|
||||
if (pType == long.class) {
|
||||
return Long.class;
|
||||
}
|
||||
if (pType == double.class) {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown type: " + pType);
|
||||
}
|
||||
|
||||
return pType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,210 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.BeanUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.util.LRUHashMap;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Converts strings to numbers and back.
|
||||
* <p/>
|
||||
* <small>This class has a static cache of {@code NumberFormats}, to avoid
|
||||
* creation and parsing of number formats every time one is used.</small>
|
||||
*
|
||||
* @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/util/convert/NumberConverter.java#2 $
|
||||
*/
|
||||
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 Map<String, Format> sFormats = new LRUHashMap<String, Format>(50);
|
||||
|
||||
public NumberConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* implementations may choose to ignore this parameter.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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.
|
||||
*
|
||||
* @see Number
|
||||
* @see java.text.NumberFormat
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
|
||||
if (StringUtil.isEmpty(pString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (pType.equals(BigInteger.class)) {
|
||||
return new BigInteger(pString); // No format?
|
||||
}
|
||||
if (pType.equals(BigDecimal.class)) {
|
||||
return new BigDecimal(pString); // No format?
|
||||
}
|
||||
|
||||
NumberFormat format;
|
||||
|
||||
if (pFormat == null) {
|
||||
// Use system default format, using default locale
|
||||
format = sDefaultFormat;
|
||||
}
|
||||
else {
|
||||
// Get format from cache
|
||||
format = getNumberFormat(pFormat);
|
||||
}
|
||||
|
||||
Number num;
|
||||
synchronized (format) {
|
||||
num = format.parse(pString);
|
||||
}
|
||||
|
||||
if (pType == Integer.TYPE || pType == Integer.class) {
|
||||
return num.intValue();
|
||||
}
|
||||
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 == Float.TYPE || pType == Float.class) {
|
||||
return num.floatValue();
|
||||
}
|
||||
else if (pType == Byte.TYPE || pType == Byte.class) {
|
||||
return num.byteValue();
|
||||
}
|
||||
else if (pType == Short.TYPE || pType == Short.class) {
|
||||
return num.shortValue();
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
catch (ParseException pe) {
|
||||
throw new ConversionException(pe);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string, using the given format
|
||||
*
|
||||
* @param pObject the object to convert.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
if (pObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(pObject instanceof Number)) {
|
||||
throw new TypeMismathException(pObject.getClass());
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert to string, default way
|
||||
if (StringUtil.isEmpty(pFormat)) {
|
||||
return sDefaultFormat.format(pObject);
|
||||
}
|
||||
|
||||
// Convert to string, using format
|
||||
NumberFormat format = getNumberFormat(pFormat);
|
||||
|
||||
synchronized (format) {
|
||||
return format.format(pObject);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
synchronized (sFormats) {
|
||||
String key = pFormatterClass.getName() + ":" + Arrays.toString(pFormat);
|
||||
Format format = sFormats.get(key);
|
||||
|
||||
if (format == null) {
|
||||
// If not found, create...
|
||||
try {
|
||||
format = (Format) BeanUtil.createInstance(pFormatterClass, pFormat);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
// ...and store in cache
|
||||
sFormats.put(key, format);
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.BeanUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.util.LRUHashMap;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Converts strings to numbers and back.
|
||||
* <p>
|
||||
* <small>This class has a static cache of {@code NumberFormats}, to avoid
|
||||
* 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 $
|
||||
* @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...
|
||||
|
||||
private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US);
|
||||
private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS);
|
||||
private static final Map<String, Format> sFormats = new LRUHashMap<String, Format>(50);
|
||||
|
||||
public NumberConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* implementations may choose to ignore this parameter.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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.
|
||||
*
|
||||
* @see Number
|
||||
* @see java.text.NumberFormat
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
|
||||
if (StringUtil.isEmpty(pString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (pType.equals(BigInteger.class)) {
|
||||
return new BigInteger(pString); // No format?
|
||||
}
|
||||
if (pType.equals(BigDecimal.class)) {
|
||||
return new BigDecimal(pString); // No format?
|
||||
}
|
||||
|
||||
NumberFormat format;
|
||||
|
||||
if (pFormat == null) {
|
||||
// Use system default format, using default locale
|
||||
format = sDefaultFormat;
|
||||
}
|
||||
else {
|
||||
// Get format from cache
|
||||
format = getNumberFormat(pFormat);
|
||||
}
|
||||
|
||||
Number num;
|
||||
synchronized (format) {
|
||||
num = format.parse(pString);
|
||||
}
|
||||
|
||||
if (pType == Integer.TYPE || pType == Integer.class) {
|
||||
return num.intValue();
|
||||
}
|
||||
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 == Float.TYPE || pType == Float.class) {
|
||||
return num.floatValue();
|
||||
}
|
||||
else if (pType == Byte.TYPE || pType == Byte.class) {
|
||||
return num.byteValue();
|
||||
}
|
||||
else if (pType == Short.TYPE || pType == Short.class) {
|
||||
return num.shortValue();
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
catch (ParseException pe) {
|
||||
throw new ConversionException(pe);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string, using the given format
|
||||
*
|
||||
* @param pObject the object to convert.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
if (pObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(pObject instanceof Number)) {
|
||||
throw new TypeMismathException(pObject.getClass());
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert to string, default way
|
||||
if (StringUtil.isEmpty(pFormat)) {
|
||||
return sDefaultFormat.format(pObject);
|
||||
}
|
||||
|
||||
// Convert to string, using format
|
||||
NumberFormat format = getNumberFormat(pFormat);
|
||||
|
||||
synchronized (format) {
|
||||
return format.format(pObject);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
synchronized (sFormats) {
|
||||
String key = pFormatterClass.getName() + ":" + Arrays.toString(pFormat);
|
||||
Format format = sFormats.get(key);
|
||||
|
||||
if (format == null) {
|
||||
// If not found, create...
|
||||
try {
|
||||
format = (Format) BeanUtil.createInstance(pFormatterClass, pFormat);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
// ...and store in cache
|
||||
sFormats.put(key, format);
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,138 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.util.Time;
|
||||
import com.twelvemonkeys.util.TimeFormat;
|
||||
|
||||
/**
|
||||
* Converts strings to times and back.
|
||||
* <p/>
|
||||
* <small>This class has a static cache of {@code TimeFormats}, to avoid creation and
|
||||
* parsing of timeformats every time one is used.</small>
|
||||
*
|
||||
* @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/util/convert/TimeConverter.java#1 $
|
||||
*/
|
||||
public class TimeConverter extends NumberConverter {
|
||||
|
||||
public TimeConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* implementations may choose to ignore this parameter.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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}
|
||||
*
|
||||
* @see com.twelvemonkeys.util.Time
|
||||
* @see com.twelvemonkeys.util.TimeFormat
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public Object toObject(String pString, Class pType, String pFormat)
|
||||
throws ConversionException {
|
||||
if (StringUtil.isEmpty(pString))
|
||||
return null;
|
||||
|
||||
TimeFormat format;
|
||||
|
||||
try {
|
||||
if (pFormat == null) {
|
||||
// Use system default format
|
||||
format = TimeFormat.getInstance();
|
||||
}
|
||||
else {
|
||||
// Get format from cache
|
||||
format = getTimeFormat(pFormat);
|
||||
}
|
||||
|
||||
return format.parse(pString);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string, using the given format
|
||||
*
|
||||
* @param pObject the object to convert.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public String toString(Object pObject, String pFormat)
|
||||
throws ConversionException {
|
||||
if (pObject == null)
|
||||
return null;
|
||||
|
||||
if (!(pObject instanceof com.twelvemonkeys.util.Time))
|
||||
throw new TypeMismathException(pObject.getClass());
|
||||
|
||||
try {
|
||||
// Convert to string, default way
|
||||
if (StringUtil.isEmpty(pFormat))
|
||||
return pObject.toString();
|
||||
|
||||
// Convert to string, using format
|
||||
TimeFormat format = getTimeFormat(pFormat);
|
||||
return format.format((Time) pObject);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
private TimeFormat getTimeFormat(String pFormat) {
|
||||
return (TimeFormat) getFormat(TimeFormat.class, pFormat);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.convert;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.util.Time;
|
||||
import com.twelvemonkeys.util.TimeFormat;
|
||||
|
||||
/**
|
||||
* Converts strings to times and back.
|
||||
* <p>
|
||||
* <small>This class has a static cache of {@code TimeFormats}, to avoid creation and
|
||||
* 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 $
|
||||
* @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 TimeConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* implementations may choose to ignore this parameter.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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}
|
||||
*
|
||||
* @see com.twelvemonkeys.util.Time
|
||||
* @see com.twelvemonkeys.util.TimeFormat
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public Object toObject(String pString, Class pType, String pFormat)
|
||||
throws ConversionException {
|
||||
if (StringUtil.isEmpty(pString))
|
||||
return null;
|
||||
|
||||
TimeFormat format;
|
||||
|
||||
try {
|
||||
if (pFormat == null) {
|
||||
// Use system default format
|
||||
format = TimeFormat.getInstance();
|
||||
}
|
||||
else {
|
||||
// Get format from cache
|
||||
format = getTimeFormat(pFormat);
|
||||
}
|
||||
|
||||
return format.parse(pString);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string, using the given format
|
||||
*
|
||||
* @param pObject the object to convert.
|
||||
* @param pFormat the format used for parsing. PropertyConverter
|
||||
* implementations may choose to ignore this parameter. Also,
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public String toString(Object pObject, String pFormat)
|
||||
throws ConversionException {
|
||||
if (pObject == null)
|
||||
return null;
|
||||
|
||||
if (!(pObject instanceof com.twelvemonkeys.util.Time))
|
||||
throw new TypeMismathException(pObject.getClass());
|
||||
|
||||
try {
|
||||
// Convert to string, default way
|
||||
if (StringUtil.isEmpty(pFormat))
|
||||
return pObject.toString();
|
||||
|
||||
// Convert to string, using format
|
||||
TimeFormat format = getTimeFormat(pFormat);
|
||||
return format.format((Time) pObject);
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
throw new ConversionException(rte);
|
||||
}
|
||||
}
|
||||
|
||||
private TimeFormat getTimeFormat(String pFormat) {
|
||||
return (TimeFormat) getFormat(TimeFormat.class, pFormat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.regex;
|
||||
|
||||
import com.twelvemonkeys.util.AbstractTokenIterator;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
/**
|
||||
* {@code StringTokenizer} replacement, that uses regular expressions to split
|
||||
* strings into tokens.
|
||||
* <p/>
|
||||
* @see java.util.regex.Pattern for pattern syntax.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class RegExTokenIterator extends AbstractTokenIterator {
|
||||
private final Matcher matcher;
|
||||
private boolean next = false;
|
||||
|
||||
/**
|
||||
* Creates a {@code RegExTokenIterator}.
|
||||
* Default pettern is {@code "\S+"}.
|
||||
*
|
||||
* @param pString the string to be parsed.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pString} is {@code null}
|
||||
*/
|
||||
public RegExTokenIterator(String pString) {
|
||||
this(pString, "\\S+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code RegExTokenIterator}.
|
||||
*
|
||||
* @see Pattern for pattern syntax.
|
||||
*
|
||||
* @param pString the string to be parsed.
|
||||
* @param pPattern the pattern
|
||||
*
|
||||
* @throws PatternSyntaxException if {@code pPattern} is not a valid pattern
|
||||
* @throws IllegalArgumentException if any of the arguments are {@code null}
|
||||
*/
|
||||
public RegExTokenIterator(String pString, String pPattern) {
|
||||
if (pString == null) {
|
||||
throw new IllegalArgumentException("string == null");
|
||||
}
|
||||
|
||||
if (pPattern == null) {
|
||||
throw new IllegalArgumentException("pattern == null");
|
||||
}
|
||||
|
||||
matcher = Pattern.compile(pPattern).matcher(pString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this iterator.
|
||||
*
|
||||
*/
|
||||
public void reset() {
|
||||
matcher.reset();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next || (next = matcher.find());
|
||||
}
|
||||
|
||||
public String next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
next = false;
|
||||
return matcher.group();
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.regex;
|
||||
|
||||
import com.twelvemonkeys.util.AbstractTokenIterator;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
/**
|
||||
* {@code StringTokenizer} replacement, that uses regular expressions to split
|
||||
* strings into tokens.
|
||||
*
|
||||
*@see java.util.regex.Pattern for pattern syntax.
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public class RegExTokenIterator extends AbstractTokenIterator {
|
||||
private final Matcher matcher;
|
||||
private boolean next = false;
|
||||
|
||||
/**
|
||||
* Creates a {@code RegExTokenIterator}.
|
||||
* Default pettern is {@code "\S+"}.
|
||||
*
|
||||
* @param pString the string to be parsed.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pString} is {@code null}
|
||||
*/
|
||||
public RegExTokenIterator(String pString) {
|
||||
this(pString, "\\S+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code RegExTokenIterator}.
|
||||
*
|
||||
* @see Pattern for pattern syntax.
|
||||
*
|
||||
* @param pString the string to be parsed.
|
||||
* @param pPattern the pattern
|
||||
*
|
||||
* @throws PatternSyntaxException if {@code pPattern} is not a valid pattern
|
||||
* @throws IllegalArgumentException if any of the arguments are {@code null}
|
||||
*/
|
||||
public RegExTokenIterator(String pString, String pPattern) {
|
||||
if (pString == null) {
|
||||
throw new IllegalArgumentException("string == null");
|
||||
}
|
||||
|
||||
if (pPattern == null) {
|
||||
throw new IllegalArgumentException("pattern == null");
|
||||
}
|
||||
|
||||
matcher = Pattern.compile(pPattern).matcher(pString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this iterator.
|
||||
*
|
||||
*/
|
||||
public void reset() {
|
||||
matcher.reset();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next || (next = matcher.find());
|
||||
}
|
||||
|
||||
public String next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
next = false;
|
||||
return matcher.group();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,63 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.service;
|
||||
|
||||
/**
|
||||
* An optional interface that may be implemented by service provider objects.
|
||||
* <p/>
|
||||
* If this interface is implemented, the service provider objects will receive
|
||||
* notification of registration and deregistration from the
|
||||
* {@code 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 $
|
||||
*/
|
||||
public interface RegisterableService {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void onRegistration(ServiceRegistry pRegistry, Class pCategory);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void onDeregistration(ServiceRegistry pRegistry, Class pCategory);
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.service;
|
||||
|
||||
/**
|
||||
* An optional interface that may be implemented by service provider objects.
|
||||
* <p>
|
||||
* If this interface is implemented, the service provider objects will receive
|
||||
* notification of registration and deregistration from the
|
||||
* {@code ServiceRegistry}.
|
||||
* </p>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public interface RegisterableService {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void onRegistration(ServiceRegistry pRegistry, Class pCategory);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void onDeregistration(ServiceRegistry pRegistry, Class pCategory);
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.service;
|
||||
|
||||
/**
|
||||
* Error thrown by the {@code ServiceRegistry} in case of a configuration
|
||||
* error.
|
||||
* <p/>
|
||||
* @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/ServiceConfigurationError.java#1 $
|
||||
*/
|
||||
public class ServiceConfigurationError extends Error {
|
||||
ServiceConfigurationError(Throwable pCause) {
|
||||
super(pCause);
|
||||
}
|
||||
|
||||
ServiceConfigurationError(String pMessage) {
|
||||
super(pMessage);
|
||||
}
|
||||
|
||||
ServiceConfigurationError(String pMessage, Throwable pCause) {
|
||||
super(pMessage, pCause);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.util.service;
|
||||
|
||||
/**
|
||||
* Error thrown by the {@code ServiceRegistry} in case of a configuration
|
||||
* error.
|
||||
*
|
||||
* @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/ServiceConfigurationError.java#1 $
|
||||
*/
|
||||
public class ServiceConfigurationError extends Error {
|
||||
ServiceConfigurationError(Throwable pCause) {
|
||||
super(pCause);
|
||||
}
|
||||
|
||||
ServiceConfigurationError(String pMessage) {
|
||||
super(pMessage);
|
||||
}
|
||||
|
||||
ServiceConfigurationError(String pMessage, Throwable pCause) {
|
||||
super(pMessage, pCause);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides a service provider registry.
|
||||
* <p/>
|
||||
* 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>.
|
||||
*
|
||||
* @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;
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides a service provider registry.
|
||||
* <p>
|
||||
* 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>.
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
|
||||
@@ -36,13 +36,12 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* BeanMapTestCase
|
||||
* <p/>
|
||||
* @todo Extend with BeanMap specific tests
|
||||
*
|
||||
* @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/test/java/com/twelvemonkeys/util/BeanMapTestCase.java#2 $
|
||||
*/
|
||||
// TODO: Extend with BeanMap specific tests
|
||||
public class BeanMapTest extends MapAbstractTest {
|
||||
|
||||
public boolean isPutAddSupported() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,188 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
|
||||
|
||||
/**
|
||||
* SVGImageReaderSpi
|
||||
* <p/>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class SVGImageReaderSpi extends ImageReaderSpiBase {
|
||||
|
||||
final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", SVGImageReaderSpi.class);
|
||||
|
||||
/**
|
||||
* Creates an {@code SVGImageReaderSpi}.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SVGImageReaderSpi() {
|
||||
super(new SVGProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
private static boolean canDecode(final ImageInputStream pInput) throws IOException {
|
||||
// NOTE: This test is quite quick as it does not involve any parsing,
|
||||
// however it may not recognize all kinds of SVG documents.
|
||||
try {
|
||||
pInput.mark();
|
||||
|
||||
// TODO: This is not ok for UTF-16 and other wide encodings
|
||||
// TODO: Use an XML (encoding) aware Reader instance instead
|
||||
// Need to figure out pretty fast if this is XML or not
|
||||
int b;
|
||||
while (Character.isWhitespace((char) (b = pInput.read()))) {
|
||||
// Skip over leading WS
|
||||
}
|
||||
|
||||
// If it's not a tag, this can't be valid XML
|
||||
if (b != '<') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Algorithm for detecting SVG:
|
||||
// - Skip until begin tag '<' and read 4 bytes
|
||||
// - 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
|
||||
// - compare next 3 bytes against "svg", return result
|
||||
// - else
|
||||
// - compare next 3 bytes against "svg", return result
|
||||
|
||||
byte[] buffer = new byte[4];
|
||||
while (true) {
|
||||
pInput.readFully(buffer);
|
||||
|
||||
if (buffer[0] == '?') {
|
||||
// This is the XML declaration or a processing instruction
|
||||
while (!((pInput.readByte() & 0xFF) == '?' && pInput.read() == '>')) {
|
||||
// Skip until end of XML declaration or processing instruction or EOF
|
||||
}
|
||||
}
|
||||
else if (buffer[0] == '!') {
|
||||
if (buffer[1] == '-' && buffer[2] == '-') {
|
||||
// This is a comment
|
||||
while (!((pInput.readByte() & 0xFF) == '-' && pInput.read() == '-' && pInput.read() == '>')) {
|
||||
// Skip until end of comment or EOF
|
||||
}
|
||||
}
|
||||
else if (buffer[1] == 'D' && buffer[2] == 'O' && buffer[3] == 'C'
|
||||
&& pInput.read() == 'T' && pInput.read() == 'Y'
|
||||
&& pInput.read() == 'P' && pInput.read() == 'E') {
|
||||
// This is the DOCTYPE declaration
|
||||
while (Character.isWhitespace((char) (b = pInput.read()))) {
|
||||
// Skip over WS
|
||||
}
|
||||
|
||||
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
|
||||
// It's SVG, identified by DOCTYPE
|
||||
return true;
|
||||
}
|
||||
|
||||
// DOCTYPE found, but not SVG
|
||||
return false;
|
||||
}
|
||||
|
||||
// Something else, we'll skip
|
||||
}
|
||||
else {
|
||||
// This is a normal tag
|
||||
if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g'
|
||||
&& (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) {
|
||||
// It's SVG, identified by root tag
|
||||
// TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)!
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the tag is not "svg", this isn't SVG
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((pInput.readByte() & 0xFF) != '<') {
|
||||
// Skip over, until next begin tag or EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EOFException ignore) {
|
||||
// Possible for small files...
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
//noinspection ThrowFromFinallyBlock
|
||||
pInput.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new SVGImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Scalable Vector Graphics (SVG) format image reader";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
// TODO: Perhaps just try to create an instance, and de-register if we fail?
|
||||
if (!SVG_READER_AVAILABLE) {
|
||||
System.err.println("Could not instantiate SVGImageReader (missing support classes).");
|
||||
|
||||
try {
|
||||
// NOTE: This will break, but it gives us some useful debug info
|
||||
new SVGImageReader(this);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
deregisterProvider(registry, this, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
|
||||
|
||||
/**
|
||||
* SVGImageReaderSpi
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class SVGImageReaderSpi extends ImageReaderSpiBase {
|
||||
|
||||
final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", SVGImageReaderSpi.class);
|
||||
|
||||
/**
|
||||
* Creates an {@code SVGImageReaderSpi}.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SVGImageReaderSpi() {
|
||||
super(new SVGProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
private static boolean canDecode(final ImageInputStream pInput) throws IOException {
|
||||
// NOTE: This test is quite quick as it does not involve any parsing,
|
||||
// however it may not recognize all kinds of SVG documents.
|
||||
try {
|
||||
pInput.mark();
|
||||
|
||||
// TODO: This is not ok for UTF-16 and other wide encodings
|
||||
// TODO: Use an XML (encoding) aware Reader instance instead
|
||||
// Need to figure out pretty fast if this is XML or not
|
||||
int b;
|
||||
while (Character.isWhitespace((char) (b = pInput.read()))) {
|
||||
// Skip over leading WS
|
||||
}
|
||||
|
||||
// If it's not a tag, this can't be valid XML
|
||||
if (b != '<') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Algorithm for detecting SVG:
|
||||
// - Skip until begin tag '<' and read 4 bytes
|
||||
// - 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
|
||||
// - compare next 3 bytes against "svg", return result
|
||||
// - else
|
||||
// - compare next 3 bytes against "svg", return result
|
||||
|
||||
byte[] buffer = new byte[4];
|
||||
while (true) {
|
||||
pInput.readFully(buffer);
|
||||
|
||||
if (buffer[0] == '?') {
|
||||
// This is the XML declaration or a processing instruction
|
||||
while (!((pInput.readByte() & 0xFF) == '?' && pInput.read() == '>')) {
|
||||
// Skip until end of XML declaration or processing instruction or EOF
|
||||
}
|
||||
}
|
||||
else if (buffer[0] == '!') {
|
||||
if (buffer[1] == '-' && buffer[2] == '-') {
|
||||
// This is a comment
|
||||
while (!((pInput.readByte() & 0xFF) == '-' && pInput.read() == '-' && pInput.read() == '>')) {
|
||||
// Skip until end of comment or EOF
|
||||
}
|
||||
}
|
||||
else if (buffer[1] == 'D' && buffer[2] == 'O' && buffer[3] == 'C'
|
||||
&& pInput.read() == 'T' && pInput.read() == 'Y'
|
||||
&& pInput.read() == 'P' && pInput.read() == 'E') {
|
||||
// This is the DOCTYPE declaration
|
||||
while (Character.isWhitespace((char) (b = pInput.read()))) {
|
||||
// Skip over WS
|
||||
}
|
||||
|
||||
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
|
||||
// It's SVG, identified by DOCTYPE
|
||||
return true;
|
||||
}
|
||||
|
||||
// DOCTYPE found, but not SVG
|
||||
return false;
|
||||
}
|
||||
|
||||
// Something else, we'll skip
|
||||
}
|
||||
else {
|
||||
// This is a normal tag
|
||||
if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g'
|
||||
&& (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) {
|
||||
// It's SVG, identified by root tag
|
||||
// TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)!
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the tag is not "svg", this isn't SVG
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((pInput.readByte() & 0xFF) != '<') {
|
||||
// Skip over, until next begin tag or EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EOFException ignore) {
|
||||
// Possible for small files...
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
//noinspection ThrowFromFinallyBlock
|
||||
pInput.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new SVGImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Scalable Vector Graphics (SVG) format image reader";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
// TODO: Perhaps just try to create an instance, and de-register if we fail?
|
||||
if (!SVG_READER_AVAILABLE) {
|
||||
System.err.println("Could not instantiate SVGImageReader (missing support classes).");
|
||||
|
||||
try {
|
||||
// NOTE: This will break, but it gives us some useful debug info
|
||||
new SVGImageReader(this);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
deregisterProvider(registry, this, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,102 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.wmf.WMFProviderInfo.WMF_READER_AVAILABLE;
|
||||
|
||||
/**
|
||||
* WMFImageReaderSpi
|
||||
* <p/>
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class WMFImageReaderSpi extends ImageReaderSpiBase {
|
||||
|
||||
/**
|
||||
* Creates a {@code WMFImageReaderSpi}.
|
||||
*/
|
||||
public WMFImageReaderSpi() {
|
||||
super(new WMFProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
return source instanceof ImageInputStream && WMF_READER_AVAILABLE && canDecode((ImageInputStream) source);
|
||||
}
|
||||
|
||||
public static boolean canDecode(final ImageInputStream pInput) throws IOException {
|
||||
if (pInput == null) {
|
||||
throw new IllegalArgumentException("input == null");
|
||||
}
|
||||
|
||||
try {
|
||||
pInput.mark();
|
||||
|
||||
for (byte header : WMF.HEADER) {
|
||||
int read = (byte) pInput.read();
|
||||
if (header != read) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
finally {
|
||||
pInput.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new WMFImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Windows Meta File (WMF) image reader";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
if (!WMF_READER_AVAILABLE) {
|
||||
IIOUtil.deregisterProvider(registry, this, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.wmf.WMFProviderInfo.WMF_READER_AVAILABLE;
|
||||
|
||||
/**
|
||||
* WMFImageReaderSpi
|
||||
*
|
||||
* @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 $
|
||||
*/
|
||||
public final class WMFImageReaderSpi extends ImageReaderSpiBase {
|
||||
|
||||
/**
|
||||
* Creates a {@code WMFImageReaderSpi}.
|
||||
*/
|
||||
public WMFImageReaderSpi() {
|
||||
super(new WMFProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
return source instanceof ImageInputStream && WMF_READER_AVAILABLE && canDecode((ImageInputStream) source);
|
||||
}
|
||||
|
||||
public static boolean canDecode(final ImageInputStream pInput) throws IOException {
|
||||
if (pInput == null) {
|
||||
throw new IllegalArgumentException("input == null");
|
||||
}
|
||||
|
||||
try {
|
||||
pInput.mark();
|
||||
|
||||
for (byte header : WMF.HEADER) {
|
||||
int read = (byte) pInput.read();
|
||||
if (header != read) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
finally {
|
||||
pInput.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new WMFImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Windows Meta File (WMF) image reader";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
if (!WMF_READER_AVAILABLE) {
|
||||
IIOUtil.deregisterProvider(registry, this, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: AbstractRLEDecoder.java#1 $
|
||||
|
||||
@@ -231,10 +231,11 @@ abstract class DIBHeader {
|
||||
|
||||
/**
|
||||
* OS/2 BitmapCoreHeader Version 2.
|
||||
* <p/>
|
||||
* <p>
|
||||
* 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
|
||||
* (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>
|
||||
*/
|
||||
@@ -290,9 +291,10 @@ abstract class DIBHeader {
|
||||
* 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
|
||||
* may use the later versions at run-time.
|
||||
* <p/>
|
||||
* <p>
|
||||
* Note: Some variations that includes the mask fields into the header size exists,
|
||||
* but is no longer part of the documented spec.
|
||||
* </p>
|
||||
*
|
||||
* @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$
|
||||
|
||||
@@ -48,14 +48,13 @@ import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* ImageReader for Microsoft Windows ICO (icon) format.
|
||||
* 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.
|
||||
* <p/>
|
||||
*
|
||||
* @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$
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Directory
|
||||
* <p/>
|
||||
*
|
||||
* @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$
|
||||
|
||||
@@ -36,7 +36,6 @@ import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @version $Id: RLE4Decoder.java#1 $
|
||||
|
||||
@@ -36,7 +36,6 @@ import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @version $Id: RLE8Decoder.java#1 $
|
||||
|
||||
@@ -72,12 +72,13 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
/**
|
||||
* Constructs an {@code ImageReader} and sets its
|
||||
* {@code originatingProvider} field to the supplied value.
|
||||
* <p/>
|
||||
* <p> Subclasses that make use of extensions should provide a
|
||||
* <p>
|
||||
* Subclasses that make use of extensions should provide a
|
||||
* constructor with signature {@code (ImageReaderSpi,
|
||||
* Object)} in order to retrieve the extension object. If
|
||||
* the extension object is unsuitable, an
|
||||
* {@code IllegalArgumentException} should be thrown.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
* <p/>
|
||||
* <p>
|
||||
* 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}.
|
||||
* </p>
|
||||
*
|
||||
* @param param an {@code ImageReadParam} to be used to get
|
||||
* 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.
|
||||
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
|
||||
* method.
|
||||
* <p/>
|
||||
* <p>
|
||||
* Note: If it is possible for the reader to read the AOI directly, such a
|
||||
* method should be used instead, for efficiency.
|
||||
* </p>
|
||||
*
|
||||
* @param pImage the image to get AOI from
|
||||
* @param pParam the param optionally specifying the AOI
|
||||
@@ -348,12 +351,14 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
* The subsampling is defined by the
|
||||
* {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)}
|
||||
* method.
|
||||
* <p/>
|
||||
* <p>
|
||||
* NOTE: This method does not take the subsampling offsets into
|
||||
* consideration.
|
||||
* <p/>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: If it is possible for the reader to subsample directly, such a
|
||||
* method should be used instead, for efficiency.
|
||||
* </p>
|
||||
*
|
||||
* @param pImage the image to subsample
|
||||
* @param pParam the param optionally specifying subsampling
|
||||
|
||||
@@ -60,12 +60,13 @@ public abstract class ImageWriterBase extends ImageWriter {
|
||||
* Constructs an {@code ImageWriter} and sets its
|
||||
* {@code originatingProvider} instance variable to the
|
||||
* supplied value.
|
||||
* <p/>
|
||||
* <p> Subclasses that make use of extensions should provide a
|
||||
* <p>
|
||||
* Subclasses that make use of extensions should provide a
|
||||
* constructor with signature {@code (ImageWriterSpi,
|
||||
* Object)} in order to retrieve the extension object. If
|
||||
* the extension object is unsuitable, an
|
||||
* {@code IllegalArgumentException} should be thrown.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
|
||||
* method.
|
||||
* <p/>
|
||||
* <p>
|
||||
* Note: If it is possible for the writer to write the AOI directly, such a
|
||||
* method should be used instead, for efficiency.
|
||||
* </p>
|
||||
*
|
||||
* @param pImage the image to get AOI from
|
||||
* @param pParam the param optionally specifying the AOI
|
||||
@@ -165,12 +167,14 @@ public abstract class ImageWriterBase extends ImageWriter {
|
||||
* The subsampling is defined by the
|
||||
* {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)}
|
||||
* method.
|
||||
* <p/>
|
||||
* <p>
|
||||
* NOTE: This method does not take the subsampling offsets into
|
||||
* consideration.
|
||||
* <p/>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: If it is possible for the writer to subsample directly, such a
|
||||
* method should be used instead, for efficiency.
|
||||
* </p>
|
||||
*
|
||||
* @param pImage the image to subsample
|
||||
* @param pParam the param optionally specifying subsampling
|
||||
|
||||
@@ -50,10 +50,11 @@ import java.util.Properties;
|
||||
|
||||
/**
|
||||
* A helper class for working with ICC color profiles and color spaces.
|
||||
* <p />
|
||||
* <p>
|
||||
* Standard ICC color profiles are read from system-specific locations
|
||||
* for known operating systems.
|
||||
* <p />
|
||||
* </p>
|
||||
* <p>
|
||||
* Color profiles may be configured by placing a property-file
|
||||
* {@code com/twelvemonkeys/imageio/color/icc_profiles.properties}
|
||||
* on the classpath, specifying the full path to the profiles.
|
||||
@@ -61,8 +62,10 @@ import java.util.Properties;
|
||||
* can be downloaded from
|
||||
* <a href="http://www.color.org/profiles2.xalter">ICC</a>,
|
||||
* <a href="http://www.adobe.com/downloads/">Adobe</a> or other places.
|
||||
* <p />
|
||||
* * </p>
|
||||
* <p>
|
||||
* Example property file:
|
||||
* </p>
|
||||
* <pre>
|
||||
* # icc_profiles.properties
|
||||
* 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.
|
||||
* <p />
|
||||
* <p>
|
||||
* For standard Java color spaces, the built-in instance is returned.
|
||||
* Otherwise, color spaces are looked up from cache and created on demand.
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @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}.
|
||||
* <p />
|
||||
* <p>
|
||||
* <em>
|
||||
* 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}.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @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.
|
||||
* Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p />
|
||||
* <p>
|
||||
* <em>
|
||||
* 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}.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return {@code profile} if valid.
|
||||
@@ -298,9 +304,10 @@ public final class ColorSpaces {
|
||||
|
||||
/**
|
||||
* Returns the color space specified by the given color space constant.
|
||||
* <p />
|
||||
* <p>
|
||||
* For standard Java color spaces, the built-in instance is returned.
|
||||
* Otherwise, color spaces are looked up from cache and created on demand.
|
||||
* </p>
|
||||
*
|
||||
* @param colorSpace the color space constant.
|
||||
* @return the {@link ColorSpace} specified by the color space constant.
|
||||
|
||||
@@ -38,9 +38,10 @@ import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* IIOInputStreamAdapter
|
||||
* <p/>
|
||||
* <p>
|
||||
* Note: You should always wrap this stream in a {@code BufferedInputStream}.
|
||||
* If not, performance may degrade significantly.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user