You are viewing a plain text version of this content. The canonical link for it is here.
Posted to sanselan-commits@incubator.apache.org by cm...@apache.org on 2008/01/23 04:45:09 UTC

svn commit: r614420 [1/3] - in /incubator/sanselan/trunk/src: main/java/org/apache/sanselan/ main/java/org/apache/sanselan/common/ main/java/org/apache/sanselan/common/byteSources/ main/java/org/apache/sanselan/formats/bmp/ main/java/org/apache/sansela...

Author: cmchen
Date: Tue Jan 22 20:44:55 2008
New Revision: 614420

URL: http://svn.apache.org/viewvc?rev=614420&view=rev
Log:
All of the unit tests are passing.  I found many bugs and closed them.  I've cleaned up a great deal of the code.  I've elaborated the exif write functionality and added sample code that demonstrates it.

Added:
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/CachingOutputStream.java   (with props)
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/GpsTest.java   (with props)
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/MakerNoteFieldTest.java   (with props)
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/SpecificExifTagTest.java   (with props)
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/TextFieldTest.java   (with props)
Removed:
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/ExifLosslessRewriteTest.java
Modified:
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/FormatCompliance.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileFunctions.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryOutputStream.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumber.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumberUtilities.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSource.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceArray.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceFile.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceInputStream.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/ico/IcoImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageMetadata.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/exifRewrite/ExifRewriter.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App13Segment.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App2Segment.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/JFIFSegment.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/Segment.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pnm/PNMImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/psd/PsdImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffDirectory.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffField.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffImageMetadata.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffReader.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/constants/ExifTagConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/constants/GPSTagConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/constants/TagConstantsUtils.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/constants/TagInfo.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/constants/TiffDirectoryConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/constants/TiffTagConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/datareaders/DataReader.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/datareaders/DataReaderTiled.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldType.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeByte.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeDouble.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeFloat.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeLong.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeRational.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeShort.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/fieldtypes/FieldTypeUnknown.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/photometricinterpreters/PhotometricInterpreterBiLevel.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/photometricinterpreters/PhotometricInterpreterLogLUV.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffImageWriterBase.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffImageWriterLossless.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffImageWriterLossy.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputDirectory.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputField.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputItem.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSet.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSummary.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/Debug.java
    incubator/sanselan/trunk/src/test/data/images/   (props changed)
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/SanselanTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/common/RationalNumberTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceDataTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceImageTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/JpegBaseTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/JpegReadTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/ExifBaseTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/ExifDumpTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/ExifRewriteTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/jpeg/exif/WriteExifMetadataExampleTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngReadTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngWriteReadTest.java
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/tiff/TiffReadTest.java

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/FormatCompliance.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/FormatCompliance.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/FormatCompliance.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/FormatCompliance.java Tue Jan 22 20:44:55 2008
@@ -43,7 +43,7 @@
 	{
 		return new FormatCompliance("ignore", false);
 	}
-	
+
 	public void addComment(String s) throws ImageReadException
 	{
 		comments.add(s);

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java Tue Jan 22 20:44:55 2008
@@ -101,22 +101,28 @@
 		return getMetadata(new ByteSourceFile(file), params);
 	}
 
-	public abstract ImageInfo getImageInfo(ByteSource byteSource)
+	public abstract ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException;
 
-	public final ImageInfo getImageInfo(byte bytes[])
+	public final ImageInfo getImageInfo(ByteSource byteSource)
 			throws ImageReadException, IOException
 	{
-		return getImageInfo(new ByteSourceArray(bytes));
+		return getImageInfo(byteSource, null);
 	}
 
-	public final ImageInfo getImageInfo(File file) throws ImageReadException,
-			IOException
+	public final ImageInfo getImageInfo(byte bytes[], Map params)
+			throws ImageReadException, IOException
+	{
+		return getImageInfo(new ByteSourceArray(bytes), params);
+	}
+
+	public final ImageInfo getImageInfo(File file, Map params)
+			throws ImageReadException, IOException
 	{
 		if (!canAcceptExtension(file))
 			return null;
 
-		return getImageInfo(new ByteSourceFile(file));
+		return getImageInfo(new ByteSourceFile(file), params);
 	}
 
 	public FormatCompliance getFormatCompliance(ByteSource byteSource)

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java Tue Jan 22 20:44:55 2008
@@ -24,4 +24,9 @@
 	{
 		super(s);
 	}
+
+	public ImageReadException(String s, Exception e)
+	{
+		super(s, e);
+	}
 }

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java Tue Jan 22 20:44:55 2008
@@ -326,13 +326,33 @@
 	 * <p>
 	 * @param  filename String.
 	 * @param  bytes  Byte array containing an image file.
+	 * @param  params Map of optional parameters, defined in SanselanConstants.
+	 * @return      An instance of ImageInfo.
+	 * @see         ImageInfo
+	 */
+	public static ImageInfo getImageInfo(String filename, byte bytes[],
+			Map params) throws ImageReadException, IOException
+	{
+		return getImageInfo(new ByteSourceArray(filename, bytes), params);
+	}
+
+	/** 
+	 * Parses the "image info" of an image.
+	 * <p>
+	 * "Image info" is a summary of basic information about the image such as: 
+	 * width, height, file format, bit depth, color type, etc.
+	 * <p>
+	 * Not to be confused with "image metadata."
+	 * <p>
+	 * @param  filename String.
+	 * @param  bytes  Byte array containing an image file.
 	 * @return      An instance of ImageInfo.
 	 * @see         ImageInfo
 	 */
 	public static ImageInfo getImageInfo(String filename, byte bytes[])
 			throws ImageReadException, IOException
 	{
-		return getImageInfo(new ByteSourceArray(filename, bytes));
+		return getImageInfo(new ByteSourceArray(filename, bytes), null);
 	}
 
 	/** 
@@ -351,7 +371,27 @@
 	public static ImageInfo getImageInfo(InputStream is, String filename)
 			throws ImageReadException, IOException
 	{
-		return getImageInfo(new ByteSourceInputStream(is, filename));
+		return getImageInfo(new ByteSourceInputStream(is, filename), null);
+	}
+
+	/** 
+	 * Parses the "image info" of an image.
+	 * <p>
+	 * "Image info" is a summary of basic information about the image such as: 
+	 * width, height, file format, bit depth, color type, etc.
+	 * <p>
+	 * Not to be confused with "image metadata."
+	 * <p>
+	 * @param  is InputStream from which to read image data.
+	 * @param  filename Filename associated with image data (optional).
+	 * @param  params Map of optional parameters, defined in SanselanConstants.
+	 * @return      An instance of ImageInfo.
+	 * @see         ImageInfo
+	 */
+	public static ImageInfo getImageInfo(InputStream is, String filename,
+			Map params) throws ImageReadException, IOException
+	{
+		return getImageInfo(new ByteSourceInputStream(is, filename), params);
 	}
 
 	/** 
@@ -369,7 +409,45 @@
 	public static ImageInfo getImageInfo(byte bytes[])
 			throws ImageReadException, IOException
 	{
-		return getImageInfo(new ByteSourceArray(bytes));
+		return getImageInfo(new ByteSourceArray(bytes), null);
+	}
+
+	/** 
+	 * Parses the "image info" of an image.
+	 * <p>
+	 * "Image info" is a summary of basic information about the image such as: 
+	 * width, height, file format, bit depth, color type, etc.
+	 * <p>
+	 * Not to be confused with "image metadata."
+	 * <p>
+	 * @param  bytes  Byte array containing an image file.
+	 * @param  params Map of optional parameters, defined in SanselanConstants.
+	 * @return      An instance of ImageInfo.
+	 * @see         ImageInfo
+	 */
+	public static ImageInfo getImageInfo(byte bytes[], Map params)
+			throws ImageReadException, IOException
+	{
+		return getImageInfo(new ByteSourceArray(bytes), params);
+	}
+
+	/** 
+	 * Parses the "image info" of an image file.
+	 * <p>
+	 * "Image info" is a summary of basic information about the image such as: 
+	 * width, height, file format, bit depth, color type, etc.
+	 * <p>
+	 * Not to be confused with "image metadata."
+	 * <p>
+	 * @param  file  File containing image data.
+	 * @param  params Map of optional parameters, defined in SanselanConstants.
+	 * @return      An instance of ImageInfo.
+	 * @see         ImageInfo
+	 */
+	public static ImageInfo getImageInfo(File file, Map params)
+			throws ImageReadException, IOException
+	{
+		return getImageInfo(new ByteSourceFile(file), params);
 	}
 
 	/** 
@@ -387,15 +465,15 @@
 	public static ImageInfo getImageInfo(File file) throws ImageReadException,
 			IOException
 	{
-		return getImageInfo(new ByteSourceFile(file));
+		return getImageInfo(file, null);
 	}
 
-	private static ImageInfo getImageInfo(ByteSource byteSource)
+	private static ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		ImageParser imageParser = getImageParser(byteSource);
 
-		ImageInfo imageInfo = imageParser.getImageInfo(byteSource);
+		ImageInfo imageInfo = imageParser.getImageInfo(byteSource, params);
 
 		return imageInfo;
 	}

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanConstants.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanConstants.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanConstants.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanConstants.java Tue Jan 22 20:44:55 2008
@@ -63,15 +63,15 @@
 
 	public static final String BUFFERED_IMAGE_FACTORY = "BUFFERED_IMAGE_FACTORY";
 
-//	/** 
-//	 * Parameter key.  Indicates whether to read embedded thumbnails.  
-//	 * <p>
-//	 * Only applies to read EXIF metadata from JPEG/JFIF files.
-//	 * <p>
-//	 * Valid values: Boolean.TRUE and Boolean.FALSE.
-//	 * <p>
-//	 * @see TiffConstants
-//	 */
-//	public static final String PARAM_KEY_READ_THUMBNAILS = "READ_THUMBNAILS";
+	/** 
+	 * Parameter key.  Indicates whether to read embedded thumbnails.  
+	 * <p>
+	 * Only applies to read EXIF metadata from JPEG/JFIF files.
+	 * <p>
+	 * Valid values: Boolean.TRUE and Boolean.FALSE.
+	 * <p>
+	 * @see TiffConstants
+	 */
+	public static final String PARAM_KEY_READ_THUMBNAILS = "READ_THUMBNAILS";
 
 }

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileFunctions.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileFunctions.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileFunctions.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileFunctions.java Tue Jan 22 20:44:55 2008
@@ -103,6 +103,18 @@
 		return true;
 	}
 
+	public final byte[] readBytes(InputStream is, int count)
+			throws ImageReadException, IOException
+	{
+		byte result[] = new byte[count];
+		for (int i = 0; i < count; i++)
+		{
+			int data = is.read();
+			result[i] = (byte) data;
+		}
+		return result;
+	}
+
 	public final void readAndVerifyBytes(InputStream is, byte expected[],
 			String exception) throws ImageReadException, IOException
 	{
@@ -115,8 +127,8 @@
 			{
 				//				System.out.println("i" + ": " + i);
 
-//				this.debugByteArray("expected", expected);
-//				debugNumber("data[" + i + "]", b);
+				//				this.debugByteArray("expected", expected);
+				//				debugNumber("data[" + i + "]", b);
 				//				debugNumber("expected[" + i + "]", expected[i]);
 
 				throw new ImageReadException(exception);
@@ -134,9 +146,9 @@
 		{
 			if (bytes[i] != expected[i])
 			{
-//				System.out.println("i" + ": " + i);
-//				debugNumber("bytes[" + i + "]", bytes[i]);
-//				debugNumber("expected[" + i + "]", expected[i]);
+				//				System.out.println("i" + ": " + i);
+				//				debugNumber("bytes[" + i + "]", bytes[i]);
+				//				debugNumber("expected[" + i + "]", expected[i]);
 
 				throw new ImageReadException(exception);
 			}
@@ -408,26 +420,30 @@
 	}
 
 	protected final int convertByteArrayToShort(String name, byte bytes[],
-			int byteOrder)
+			int byteOrder) throws ImageReadException
 	{
 		return convertByteArrayToShort(name, 0, bytes, byteOrder);
 	}
 
-	protected final int convertByteArrayToShort(String name, int start,
-			byte bytes[], int byteOrder)
+	protected final int convertByteArrayToShort(String name, int index,
+			byte bytes[], int byteOrder) throws ImageReadException
 	{
-		byte byte0 = bytes[start + 0];
-		byte byte1 = bytes[start + 1];
+		if (index + 1 >= bytes.length)
+			throw new ImageReadException("Index out of bounds. Array size: "
+					+ bytes.length + ", index: " + index);
+
+		int byte0 = 0xff & bytes[index + 0];
+		int byte1 = 0xff & bytes[index + 1];
 
 		//		return convert2BytesToShort(name, byte0, byte1, byteOrder);
 
 		int result;
 
 		if (byteOrder == BYTE_ORDER_MOTOROLA) // motorola, big endian
-			result = ((0xff & byte0) << 8) + ((0xff & byte1) << 0);
+			result = (byte0 << 8) | byte1;
 		else
 			// intel, little endian
-			result = ((0xff & byte1) << 8) + ((0xff & byte0) << 0);
+			result = (byte1 << 8) | byte0;
 
 		if (debug)
 			debugNumber(name, result, 2);
@@ -437,6 +453,8 @@
 
 	protected final int[] convertByteArrayToShortArray(String name,
 			byte bytes[], int start, int length, int byteOrder)
+			throws ImageReadException
+
 	{
 		int expectedLength = start + length * 2;
 
@@ -558,8 +576,8 @@
 	{
 		if (a.length != b.length)
 		{
-			System.out.println("length mismatch: " + a.length + " != "
-					+ b.length);
+//			System.out.println("length mismatch: " + a.length + " != "
+//					+ b.length);
 			return false;
 		}
 
@@ -580,11 +598,36 @@
 		{
 			if (a[aStart + i] != b[bStart + i])
 			{
-				debugNumber("\t" + "a[" + (aStart + i) + "]", a[aStart + i]);
-				debugNumber("\t" + "b[" + (bStart + i) + "]", b[bStart + i]);
+//				debugNumber("\t" + "a[" + (aStart + i) + "]", a[aStart + i]);
+//				debugNumber("\t" + "b[" + (bStart + i) + "]", b[bStart + i]);
 
 				return false;
 			}
+		}
+
+		return true;
+	}
+
+	public static final boolean compareBytes(byte a[], byte b[])
+	{
+		if (a.length != b.length)
+			return false;
+
+		return compareBytes(a, 0, b, 0, a.length);
+	}
+
+	public static final boolean compareBytes(byte a[], int aStart, byte b[],
+			int bStart, int length)
+	{
+		if (a.length < (aStart + length))
+			return false;
+		if (b.length < (bStart + length))
+			return false;
+
+		for (int i = 0; i < length; i++)
+		{
+			if (a[aStart + i] != b[bStart + i])
+				return false;
 		}
 
 		return true;

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryFileParser.java Tue Jan 22 20:44:55 2008
@@ -69,12 +69,13 @@
 	}
 
 	public final int convertByteArrayToShort(String name, byte bytes[])
+			throws ImageReadException
 	{
 		return convertByteArrayToShort(name, bytes, byteOrder);
 	}
 
 	public final int convertByteArrayToShort(String name, int start,
-			byte bytes[])
+			byte bytes[]) throws ImageReadException
 	{
 		return convertByteArrayToShort(name, start, bytes, byteOrder);
 	}

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryOutputStream.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryOutputStream.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryOutputStream.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryOutputStream.java Tue Jan 22 20:44:55 2008
@@ -71,7 +71,7 @@
 		this.byteOrder = byteOrder;
 	}
 
-	protected int getByteOrder()
+	public int getByteOrder()
 	{
 		return byteOrder;
 	}

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumber.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumber.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumber.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumber.java Tue Jan 22 20:44:55 2008
@@ -19,8 +19,6 @@
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
 
-import org.apache.sanselan.util.Debug;
-
 public class RationalNumber extends Number
 {
 	private static final long serialVersionUID = -1;

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumberUtilities.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumberUtilities.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumberUtilities.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/RationalNumberUtilities.java Tue Jan 22 20:44:55 2008
@@ -16,7 +16,6 @@
  */
 package org.apache.sanselan.common;
 
-
 public abstract class RationalNumberUtilities extends Number
 {
 

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSource.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSource.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSource.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSource.java Tue Jan 22 20:44:55 2008
@@ -45,7 +45,13 @@
 
 	public abstract byte[] getAll() throws IOException;
 
-	//	public abstract long getLength();
+	/*
+	 * This operation can be VERY expensive; for inputstream 
+	 * byte sources, the entire stream must be drained to 
+	 * determine its length.
+	 */
+	public abstract long getLength() throws IOException;
+
 	//
 	//	public byte[] getAll() throws IOException
 	//	{

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceArray.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceArray.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceArray.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceArray.java Tue Jan 22 20:44:55 2008
@@ -53,10 +53,10 @@
 		return result;
 	}
 
-	//	public long getLength()
-	//	{
-	//		return bytes.length;
-	//	}
+		public long getLength()
+	{
+		return bytes.length;
+	}
 
 	public byte[] getAll() throws IOException
 	{

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceFile.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceFile.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceFile.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceFile.java Tue Jan 22 20:44:55 2008
@@ -70,12 +70,10 @@
 		}
 	}
 
-	//	public long getLength()
-	//	{
-	//		//		System.out.println("file.length(): " + file.length());
-	//
-	//		return file.length();
-	//	}
+	public long getLength()
+	{
+		return file.length();
+	}
 
 	public byte[] getAll() throws IOException
 	{

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceInputStream.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceInputStream.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceInputStream.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/byteSources/ByteSourceInputStream.java Tue Jan 22 20:44:55 2008
@@ -185,12 +185,21 @@
 		}
 	}
 
-	//	public long getLength()
-	//	{
-	//		//		System.out.println("file.length(): " + file.length());
-	//
-	//		return file.length();
-	//	}
+	private Long length = null;
+
+	public long getLength() throws IOException
+	{
+		if (length != null)
+			return length.longValue();
+
+		InputStream is = getInputStream();
+		long result = 0;
+		long skipped;
+		while ((skipped = is.skip(1024)) > 0)
+			result += skipped;
+		length = new Long(result);
+		return result;
+	}
 
 	public byte[] getAll() throws IOException
 	{

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java Tue Jan 22 20:44:55 2008
@@ -455,7 +455,7 @@
 		return "Unknown";
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		ImageContents ic = readImageContents(byteSource.getInputStream(),
@@ -524,7 +524,7 @@
 	{
 		pw.println("bmp.dumpImageFile");
 
-		ImageInfo imageData = getImageInfo(byteSource);
+		ImageInfo imageData = getImageInfo(byteSource, null);
 		if (imageData == null)
 			return false;
 

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java Tue Jan 22 20:44:55 2008
@@ -482,8 +482,8 @@
 			boolean stop_before_image_data) throws ImageReadException,
 			IOException
 	{
-		return readFile(byteSource, stop_before_image_data,
-				FormatCompliance.getDefault());
+		return readFile(byteSource, stop_before_image_data, FormatCompliance
+				.getDefault());
 	}
 
 	private ImageContents readFile(ByteSource byteSource,
@@ -578,7 +578,7 @@
 		return result;
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		ImageContents blocks = readFile(byteSource, false);

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/ico/IcoImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/ico/IcoImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/ico/IcoImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/ico/IcoImageParser.java Tue Jan 22 20:44:55 2008
@@ -85,7 +85,7 @@
 		return null;
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		return null;

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java Tue Jan 22 20:44:55 2008
@@ -20,7 +20,18 @@
 {
 
 	public static final byte JFIF0_SIGNATURE[] = new byte[]{
-			(byte) 'J', (byte) 'F', (byte) 'I', (byte) 'F', (byte) 0,
+			0x4a, // J
+			0x46, // F
+			0x49, // I
+			0x46, // F
+			0x0, //  
+	};
+	public static final byte JFIF0_SIGNATURE_ALTERNATIVE[] = new byte[]{
+			0x4a, // J
+			0x46, // F
+			0x49, // I
+			0x46, // F
+			0x20, //  
 	};
 
 	public static final byte ExifIdentifierCode[] = {

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageMetadata.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageMetadata.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageMetadata.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageMetadata.java Tue Jan 22 20:44:55 2008
@@ -23,8 +23,8 @@
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.common.IImageMetadata;
 import org.apache.sanselan.common.ImageMetadata;
-import org.apache.sanselan.formats.tiff.TiffImageData;
 import org.apache.sanselan.formats.tiff.TiffField;
+import org.apache.sanselan.formats.tiff.TiffImageData;
 import org.apache.sanselan.formats.tiff.TiffImageMetadata;
 import org.apache.sanselan.formats.tiff.constants.TagInfo;
 import org.apache.sanselan.util.Debug;
@@ -73,7 +73,8 @@
 		return null;
 	}
 
-	public BufferedImage getEXIFThumbnail() throws ImageReadException, IOException
+	public BufferedImage getEXIFThumbnail() throws ImageReadException,
+			IOException
 	{
 		ArrayList dirs = exif.getDirectories();
 		for (int i = 0; i < dirs.size(); i++)

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java Tue Jan 22 20:44:55 2008
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.sanselan.ImageFormat;
@@ -43,9 +44,13 @@
 import org.apache.sanselan.formats.tiff.TiffField;
 import org.apache.sanselan.formats.tiff.TiffImageMetadata;
 import org.apache.sanselan.formats.tiff.TiffImageParser;
+import org.apache.sanselan.formats.tiff.constants.TiffTagConstants;
 import org.apache.sanselan.util.Debug;
 
-public class JpegImageParser extends ImageParser implements JpegConstants
+public class JpegImageParser extends ImageParser
+		implements
+			JpegConstants,
+			TiffTagConstants
 {
 	public JpegImageParser()
 	{
@@ -130,11 +135,16 @@
 				if (marker == 0xffd9)
 					return false;
 
+				//				Debug.debug("visitSegment marker", marker);
+				////				Debug.debug("visitSegment keepMarker(marker, markers)", keepMarker(marker, markers));
+				//				Debug.debug("visitSegment keepMarker(marker, markers)", keepMarker(marker, markers));
+
 				if (!keepMarker(marker, markers))
 					return true;
 
 				if (marker == JPEG_APP13_Marker)
 				{
+					//					Debug.debug("app 13 segment data", segmentData.length);
 					result.add(new App13Segment(parser, marker, segmentData));
 				}
 				else if (marker == JPEG_APP2_Marker)
@@ -332,6 +342,21 @@
 		return result;
 	}
 
+	private ArrayList filterSegments(ArrayList v, List markers)
+	{
+		ArrayList result = new ArrayList();
+
+		for (int i = 0; i < v.size(); i++)
+		{
+			Segment segment = (Segment) v.get(i);
+			Integer marker = new Integer(segment.marker);
+			if (markers.contains(marker))
+				result.add(segment);
+		}
+
+		return result;
+	}
+
 	public TiffImageMetadata getExifMetadata(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
@@ -339,10 +364,14 @@
 		if (null == bytes)
 			return null;
 
-		if(params==null)
+		if (params == null)
 			params = new HashMap();
-//		if (!params.containsKey(PARAM_KEY_READ_THUMBNAILS))
-//			params.put(PARAM_KEY_READ_THUMBNAILS, Boolean.TRUE);
+		if (!params.containsKey(PARAM_KEY_READ_THUMBNAILS))
+			params.put(PARAM_KEY_READ_THUMBNAILS, Boolean.TRUE);
+
+		//		Debug.debug("read thumbs?", params.get(PARAM_KEY_READ_THUMBNAILS));
+
+		//				Debug.debug("exif bytes", bytes.length);
 
 		return (TiffImageMetadata) new TiffImageParser().getMetadata(bytes,
 				params);
@@ -363,7 +392,12 @@
 			System.out.println("exif_segments.size" + ": "
 					+ exifSegments.size());
 
+		//		Debug.debug("segments", segments);
+		//		Debug.debug("exifSegments", exifSegments);
+
 		// TODO: concatenate if multiple segments, need example.
+		if (exifSegments.size() < 1)
+			return null;
 		if (exifSegments.size() > 1)
 			throw new ImageReadException(
 					"Sanselan currently can't parse EXIF metadata split across multiple APP1 segments.  "
@@ -379,6 +413,52 @@
 		return getByteArrayTail("trimmed exif bytes", bytes, 6);
 	}
 
+	public boolean hasExifSegment(ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		final boolean result[] = {
+			false,
+		};
+
+		JpegUtils.Visitor visitor = new JpegUtils.Visitor()
+		{
+			// return false to exit before reading image data.
+			public boolean beginSOS()
+			{
+				return false;
+			}
+
+			public void visitSOS(int marker, byte markerBytes[],
+					byte imageData[])
+			{
+			}
+
+			// return false to exit traversal.
+			public boolean visitSegment(int marker, byte markerBytes[],
+					int markerLength, byte markerLengthBytes[],
+					byte segmentData[]) throws ImageReadException, IOException
+			{
+				if (marker == 0xffd9)
+					return false;
+
+				if (marker == JPEG_APP1_Marker)
+				{
+					if (byteArrayHasPrefix(segmentData, ExifIdentifierCode))
+					{
+						result[0] = true;
+						return false;
+					}
+				}
+
+				return true;
+			}
+		};
+
+		new JpegUtils().traverseJFIF(byteSource, visitor);
+
+		return result[0];
+	}
+
 	private JpegImageMetadata.Photoshop getPhotoshopMetadata(
 			ByteSource byteSource) throws ImageReadException, IOException
 	{
@@ -441,9 +521,10 @@
 		return false;
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
+		//		ArrayList allSegments = readSegments(byteSource, null, false);
 
 		ArrayList SOF_segments = readSegments(byteSource, new int[]{
 				//				kJFIFMarker,
@@ -461,7 +542,7 @@
 		//			System.out.println("Incoherent SOFN Data Found: "
 		//					+ SOF_segments.size());
 
-		ArrayList JFIF_segments = readSegments(byteSource, new int[]{
+		ArrayList jfifSegments = readSegments(byteSource, new int[]{
 			JFIFMarker,
 		}, true);
 
@@ -474,10 +555,10 @@
 		int Width = fSOFNSegment.width;
 		int Height = fSOFNSegment.height;
 
-		JFIFSegment fTheJFIFSegment = null;
+		JFIFSegment jfifSegment = null;
 
-		if ((JFIF_segments != null) && (JFIF_segments.size() > 0))
-			fTheJFIFSegment = (JFIFSegment) JFIF_segments.get(0);
+		if ((jfifSegments != null) && (jfifSegments.size() > 0))
+			jfifSegment = (JFIFSegment) jfifSegments.get(0);
 
 		//		JFIFSegment fTheJFIFSegment = (JFIFSegment) findSegment(segments,
 		//				kJFIFMarker);
@@ -489,16 +570,16 @@
 		//		int JFIF_minor_version;
 		String FormatDetails;
 
-		if (fTheJFIFSegment != null)
+		if (jfifSegment != null)
 		{
-			x_density = fTheJFIFSegment.xDensity;
-			y_density = fTheJFIFSegment.yDensity;
-			int density_units = fTheJFIFSegment.densityUnits;
+			x_density = jfifSegment.xDensity;
+			y_density = jfifSegment.yDensity;
+			int density_units = jfifSegment.densityUnits;
 			//			JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
 			//			JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
 
-			FormatDetails = "Jpeg/JFIF v." + fTheJFIFSegment.jfifMajorVersion
-					+ "." + fTheJFIFSegment.jfifMinorVersion;
+			FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion + "."
+					+ jfifSegment.jfifMinorVersion;
 
 			switch (density_units)
 			{
@@ -516,46 +597,47 @@
 		}
 		else
 		{
-			JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(byteSource);
-
-			{
-				TiffField field = metadata
-						.findEXIFValue(TiffField.TIFF_TAG_XRESOLUTION);
-				if (field == null)
-					throw new ImageReadException("No XResolution");
-
-				x_density = ((Number) field.getValue()).doubleValue();
-			}
-			{
-				TiffField field = metadata
-						.findEXIFValue(TiffField.TIFF_TAG_YRESOLUTION);
-				if (field == null)
-					throw new ImageReadException("No YResolution");
+			JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(
+					byteSource, params);
 
-				y_density = ((Number) field.getValue()).doubleValue();
-			}
+			if (metadata != null)
 			{
-				TiffField field = metadata
-						.findEXIFValue(TiffField.TIFF_TAG_RESOLUTION_UNIT);
-				if (field == null)
-					throw new ImageReadException("No ResolutionUnits");
-
-				int density_units = ((Number) field.getValue()).intValue();
-
-				switch (density_units)
 				{
-					case 1 :
-						break;
-					case 2 : // inches
-						units_per_inch = 1.0;
-						break;
-					case 3 : // cms
-						units_per_inch = 2.54;
-						break;
-					default :
-						break;
+					TiffField field = metadata
+							.findEXIFValue(TIFF_TAG_XRESOLUTION);
+					if (field != null)
+						x_density = ((Number) field.getValue()).doubleValue();
+				}
+				{
+					TiffField field = metadata
+							.findEXIFValue(TIFF_TAG_YRESOLUTION);
+					if (field != null)
+						y_density = ((Number) field.getValue()).doubleValue();
 				}
+				{
+					TiffField field = metadata
+							.findEXIFValue(TIFF_TAG_RESOLUTION_UNIT);
+					if (field != null)
+					{
+						int density_units = ((Number) field.getValue())
+								.intValue();
+
+						switch (density_units)
+						{
+							case 1 :
+								break;
+							case 2 : // inches
+								units_per_inch = 1.0;
+								break;
+							case 3 : // cms
+								units_per_inch = 2.54;
+								break;
+							default :
+								break;
+						}
+					}
 
+				}
 			}
 
 			FormatDetails = "Jpeg/DCM";
@@ -581,14 +663,14 @@
 		// TODO: comments...
 
 		int Number_of_components = fSOFNSegment.numberOfComponents;
-		int Precision = fSOFNSegment.Precision;
+		int Precision = fSOFNSegment.precision;
 
 		int BitsPerPixel = Number_of_components * Precision;
 		ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
 		String FormatName = "JPEG (Joint Photographic Experts Group) Format";
 		String MimeType = "image/jpeg";
 		// we ought to count images, but don't yet.
-		int NumberOfImages = -1;
+		int NumberOfImages = 1;
 		// not accurate ... only reflects first
 		boolean isProgressive = fSOFNSegment.marker == SOF2Marker;
 
@@ -614,6 +696,172 @@
 
 		return result;
 	}
+
+	//	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
+	//			throws ImageReadException, IOException
+	//	{
+	//
+	//		ArrayList allSegments = readSegments(byteSource, null, false);
+	//
+	//		final int SOF_MARKERS[] = new int[]{
+	//				SOF0Marker, SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker,
+	//				SOF6Marker, SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker,
+	//				SOF13Marker, SOF14Marker, SOF15Marker,
+	//		};
+	//
+	//		ArrayList sofMarkers = new ArrayList();
+	//		for(int i=0;i<SOF_MARKERS.length;i++)
+	//			sofMarkers.add(new Integer(SOF_MARKERS[i]));
+	//		ArrayList SOFSegments = filterSegments(allSegments, sofMarkers);
+	//		if (SOFSegments == null || SOFSegments.size()<1)
+	//			throw new ImageReadException("No SOFN Data Found.");
+	//
+	//		List jfifMarkers = new ArrayList();
+	//		jfifMarkers.add(new Integer(JFIFMarker));
+	//		ArrayList jfifSegments = filterSegments(allSegments, jfifMarkers);
+	//
+	//		SOFNSegment firstSOFNSegment = (SOFNSegment) SOFSegments.get(0);
+	//
+	//		int Width = firstSOFNSegment.width;
+	//		int Height = firstSOFNSegment.height;
+	//
+	//		JFIFSegment jfifSegment = null;
+	//
+	//		if (jfifSegments != null && jfifSegments.size() > 0)
+	//			jfifSegment = (JFIFSegment) jfifSegments.get(0);
+	//
+	//		double x_density = -1.0;
+	//		double y_density = -1.0;
+	//		double units_per_inch = -1.0;
+	//		//		int JFIF_major_version;
+	//		//		int JFIF_minor_version;
+	//		String FormatDetails;
+	//
+	//		if (jfifSegment != null)
+	//		{
+	//			x_density = jfifSegment.xDensity;
+	//			y_density = jfifSegment.yDensity;
+	//			int density_units = jfifSegment.densityUnits;
+	//			//			JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
+	//			//			JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
+	//
+	//			FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion
+	//					+ "." + jfifSegment.jfifMinorVersion;
+	//
+	//			switch (density_units)
+	//			{
+	//				case 0 :
+	//					break;
+	//				case 1 : // inches
+	//					units_per_inch = 1.0;
+	//					break;
+	//				case 2 : // cms
+	//					units_per_inch = 2.54;
+	//					break;
+	//				default :
+	//					break;
+	//			}
+	//		}
+	//		else
+	//		{
+	//			JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(byteSource, params);
+	//
+	//			{
+	//				TiffField field = metadata
+	//						.findEXIFValue(TiffField.TIFF_TAG_XRESOLUTION);
+	//				if (field == null)
+	//					throw new ImageReadException("No XResolution");
+	//
+	//				x_density = ((Number) field.getValue()).doubleValue();
+	//			}
+	//			{
+	//				TiffField field = metadata
+	//						.findEXIFValue(TiffField.TIFF_TAG_YRESOLUTION);
+	//				if (field == null)
+	//					throw new ImageReadException("No YResolution");
+	//
+	//				y_density = ((Number) field.getValue()).doubleValue();
+	//			}
+	//			{
+	//				TiffField field = metadata
+	//						.findEXIFValue(TiffField.TIFF_TAG_RESOLUTION_UNIT);
+	//				if (field == null)
+	//					throw new ImageReadException("No ResolutionUnits");
+	//
+	//				int density_units = ((Number) field.getValue()).intValue();
+	//
+	//				switch (density_units)
+	//				{
+	//					case 1 :
+	//						break;
+	//					case 2 : // inches
+	//						units_per_inch = 1.0;
+	//						break;
+	//					case 3 : // cms
+	//						units_per_inch = 2.54;
+	//						break;
+	//					default :
+	//						break;
+	//				}
+	//
+	//			}
+	//
+	//			FormatDetails = "Jpeg/DCM";
+	//
+	//		}
+	//
+	//		int PhysicalHeightDpi = -1;
+	//		float PhysicalHeightInch = -1;
+	//		int PhysicalWidthDpi = -1;
+	//		float PhysicalWidthInch = -1;
+	//
+	//		if (units_per_inch > 0)
+	//		{
+	//			PhysicalWidthDpi = (int) Math.round((double) x_density
+	//					/ units_per_inch);
+	//			PhysicalWidthInch = (float) ((double) Width / (x_density * units_per_inch));
+	//			PhysicalHeightDpi = (int) Math.round((double) y_density
+	//					* units_per_inch);
+	//			PhysicalHeightInch = (float) ((double) Height / (y_density * units_per_inch));
+	//		}
+	//
+	//		ArrayList Comments = new ArrayList();
+	//		// TODO: comments...
+	//
+	//		int Number_of_components = firstSOFNSegment.numberOfComponents;
+	//		int Precision = firstSOFNSegment.precision;
+	//
+	//		int BitsPerPixel = Number_of_components * Precision;
+	//		ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
+	//		String FormatName = "JPEG (Joint Photographic Experts Group) Format";
+	//		String MimeType = "image/jpeg";
+	//		// we ought to count images, but don't yet.
+	//		int NumberOfImages = -1;
+	//		// not accurate ... only reflects first
+	//		boolean isProgressive = firstSOFNSegment.marker == SOF2Marker;
+	//
+	//		boolean isTransparent = false; // TODO: inaccurate.
+	//		boolean usesPalette = false; // TODO: inaccurate.
+	//		int ColorType;
+	//		if (Number_of_components == 1)
+	//			ColorType = ImageInfo.COLOR_TYPE_BW;
+	//		else if (Number_of_components == 3)
+	//			ColorType = ImageInfo.COLOR_TYPE_RGB;
+	//		else if (Number_of_components == 4)
+	//			ColorType = ImageInfo.COLOR_TYPE_CMYK;
+	//		else
+	//			ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;
+	//
+	//		String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
+	//
+	//		ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
+	//				Format, FormatName, Height, MimeType, NumberOfImages,
+	//				PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
+	//				PhysicalWidthInch, Width, isProgressive, isTransparent,
+	//				usesPalette, ColorType, compressionAlgorithm);
+	//
+	//		return result;
+	//	}
 
 	public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
 			throws ImageReadException, IOException

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java Tue Jan 22 20:44:55 2008
@@ -89,6 +89,8 @@
 						markerLength - 2, is,
 						"Invalid Segment: insufficient data");
 
+				//				Debug.debug("markerLength", markerLength);
+
 				if (!visitor.visitSegment(marker, markerBytes, markerLength,
 						markerLengthBytes, segmentData))
 					return;

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/exifRewrite/ExifRewriter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/exifRewrite/ExifRewriter.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/exifRewrite/ExifRewriter.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/exifRewrite/ExifRewriter.java Tue Jan 22 20:44:55 2008
@@ -22,6 +22,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.ImageWriteException;
@@ -32,8 +33,6 @@
 import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
 import org.apache.sanselan.formats.jpeg.JpegConstants;
 import org.apache.sanselan.formats.jpeg.JpegUtils;
-import org.apache.sanselan.formats.jpeg.segments.GenericSegment;
-import org.apache.sanselan.formats.jpeg.segments.UnknownSegment;
 import org.apache.sanselan.formats.tiff.write.TiffImageWriterBase;
 import org.apache.sanselan.formats.tiff.write.TiffImageWriterLossless;
 import org.apache.sanselan.formats.tiff.write.TiffImageWriterLossy;
@@ -72,14 +71,73 @@
 
 	private static class JFIFPieces
 	{
-		public final ArrayList pieces;
-		public final GenericSegment exifSegment;
+		public final List pieces;
+		public final List exifPieces;
 
-		public JFIFPieces(final ArrayList pieces,
-				final GenericSegment exifSegment)
+		public JFIFPieces(final List pieces, final List exifPieces)
 		{
 			this.pieces = pieces;
-			this.exifSegment = exifSegment;
+			this.exifPieces = exifPieces;
+		}
+
+	}
+
+	private abstract static class JFIFPiece
+	{
+		protected abstract void write(OutputStream os) throws IOException;
+	}
+
+	private static class JFIFPieceSegment extends JFIFPiece
+	{
+		public final int marker;
+		public final byte markerBytes[];
+		public final byte markerLengthBytes[];
+		public final byte segmentData[];
+
+		public JFIFPieceSegment(final int marker, final byte[] markerBytes,
+				final byte[] markerLengthBytes, final byte[] segmentData)
+		{
+			this.marker = marker;
+			this.markerBytes = markerBytes;
+			this.markerLengthBytes = markerLengthBytes;
+			this.segmentData = segmentData;
+		}
+
+		protected void write(OutputStream os) throws IOException
+		{
+			os.write(markerBytes);
+			os.write(markerLengthBytes);
+			os.write(segmentData);
+		}
+	}
+
+	private static class JFIFPieceSegmentExif extends JFIFPieceSegment
+	{
+
+		public JFIFPieceSegmentExif(final int marker, final byte[] markerBytes,
+				final byte[] markerLengthBytes, final byte[] segmentData)
+		{
+			super(marker, markerBytes, markerLengthBytes, segmentData);
+		}
+	}
+
+	private static class JFIFPieceImageData extends JFIFPiece
+	{
+		public final byte markerBytes[];
+		public final byte imageData[];
+
+		public JFIFPieceImageData(final byte[] markerBytes,
+				final byte[] imageData)
+		{
+			super();
+			this.markerBytes = markerBytes;
+			this.imageData = imageData;
+		}
+
+		protected void write(OutputStream os) throws IOException
+		{
+			os.write(markerBytes);
+			os.write(imageData);
 		}
 	}
 
@@ -88,9 +146,7 @@
 	//			, ImageWriteException
 	{
 		final ArrayList pieces = new ArrayList();
-		final GenericSegment exifSegmentArray[] = new GenericSegment[]{
-			null,
-		};
+		final List exifPieces = new ArrayList();
 
 		JpegUtils.Visitor visitor = new JpegUtils.Visitor()
 		{
@@ -103,8 +159,7 @@
 			public void visitSOS(int marker, byte markerBytes[],
 					byte imageData[])
 			{
-				pieces.add(markerBytes);
-				pieces.add(imageData);
+				pieces.add(new JFIFPieceImageData(markerBytes, imageData));
 			}
 
 			// return false to exit traversal.
@@ -116,29 +171,26 @@
 			{
 				if (marker != JPEG_APP1_Marker)
 				{
-					pieces.add(markerBytes);
-					pieces.add(markerLengthBytes);
-					pieces.add(segmentData);
+					pieces.add(new JFIFPieceSegment(marker, markerBytes,
+							markerLengthBytes, segmentData));
 				}
 				else if (!byteArrayHasPrefix(segmentData, ExifIdentifierCode))
 				{
-					pieces.add(markerBytes);
-					pieces.add(markerLengthBytes);
-					pieces.add(segmentData);
-				}
-				else if (exifSegmentArray[0] != null)
-				{
-					// TODO: add support for multiple segments
-					throw new ImageReadException(
-							"More than one APP1 EXIF segment.");
+					pieces.add(new JFIFPieceSegment(marker, markerBytes,
+							markerLengthBytes, segmentData));
 				}
+				//				else if (exifSegmentArray[0] != null)
+				//				{
+				//					// TODO: add support for multiple segments
+				//					throw new ImageReadException(
+				//							"More than one APP1 EXIF segment.");
+				//				}
 				else
 				{
-					UnknownSegment segment = new UnknownSegment(marker,
-							segmentData);
-
-					exifSegmentArray[0] = segment;
-					pieces.add(segment);
+					JFIFPiece piece = new JFIFPieceSegmentExif(marker,
+							markerBytes, markerLengthBytes, segmentData);
+					pieces.add(piece);
+					exifPieces.add(piece);
 				}
 				return true;
 			}
@@ -146,14 +198,14 @@
 
 		new JpegUtils().traverseJFIF(byteSource, visitor);
 
-		GenericSegment exifSegment = exifSegmentArray[0];
-		if (exifSegment == null)
-		{
-			// TODO: add support for adding, not just replacing.
-			throw new ImageReadException("No APP1 EXIF segment found.");
-		}
+		//		GenericSegment exifSegment = exifSegmentArray[0];
+		//		if (exifSegments.size() < 1)
+		//		{
+		//			// TODO: add support for adding, not just replacing.
+		//			throw new ImageReadException("No APP1 EXIF segment found.");
+		//		}
 
-		return new JFIFPieces(pieces, exifSegment);
+		return new JFIFPieces(pieces, exifPieces);
 	}
 
 	/** 
@@ -214,9 +266,13 @@
 			throws ImageReadException, IOException, ImageWriteException
 	{
 		JFIFPieces jfifPieces = analyzeJFIF(byteSource);
-		ArrayList pieces = jfifPieces.pieces;
+		List pieces = jfifPieces.pieces;
 
-		pieces.remove(jfifPieces.exifSegment);
+		//		Debug.debug("pieces", pieces);
+
+		//		pieces.removeAll(jfifPieces.exifSegments);
+
+		//		Debug.debug("pieces", pieces);
 
 		writeSegmentsReplacingExif(os, pieces, null);
 	}
@@ -303,10 +359,16 @@
 	{
 		//		List outputDirectories = outputSet.getDirectories();
 		JFIFPieces jfifPieces = analyzeJFIF(byteSource);
-		ArrayList pieces = jfifPieces.pieces;
-		GenericSegment exifSegment = jfifPieces.exifSegment;
+		List pieces = jfifPieces.pieces;
+
+		JFIFPieceSegment exifPiece = null;
+
+		// Just use first APP1 segment for now.
+		// Multiple APP1 segments are rare and poorly supported.
+		if (jfifPieces.exifPieces.size() > 0)
+			exifPiece = (JFIFPieceSegment) jfifPieces.exifPieces.get(0);
 
-		byte exifBytes[] = exifSegment.bytes;
+		byte exifBytes[] = exifPiece.segmentData;
 		exifBytes = getByteArrayTail("trimmed exif bytes", exifBytes, 6);
 
 		TiffImageWriterBase writer = new TiffImageWriterLossless(
@@ -387,7 +449,7 @@
 			ImageWriteException
 	{
 		JFIFPieces jfifPieces = analyzeJFIF(byteSource);
-		ArrayList pieces = jfifPieces.pieces;
+		List pieces = jfifPieces.pieces;
 
 		TiffImageWriterBase writer = new TiffImageWriterLossy(
 				outputSet.byteOrder);
@@ -398,9 +460,8 @@
 		writeSegmentsReplacingExif(os, pieces, newBytes);
 	}
 
-	private void writeSegmentsReplacingExif(OutputStream os,
-			ArrayList segments, byte newBytes[]) throws ImageWriteException,
-			IOException
+	private void writeSegmentsReplacingExif(OutputStream os, List segments,
+			byte newBytes[]) throws ImageWriteException, IOException
 	{
 		int byteOrder = getByteOrder();
 
@@ -408,33 +469,67 @@
 		{
 			os.write(SOI);
 
+			boolean hasExif = false;
+
 			for (int i = 0; i < segments.size(); i++)
 			{
-				Object o = segments.get(i);
-				if (o instanceof byte[])
-				{
-					byte bytes[] = (byte[]) o;
-					os.write(bytes);
-				}
-				else if (o instanceof GenericSegment)
+				JFIFPiece piece = (JFIFPiece) segments.get(i);
+				if (piece instanceof JFIFPieceSegmentExif)
+					hasExif = true;
+			}
+
+			if (!hasExif && newBytes != null)
+			{
+				byte markerBytes[] = convertShortToByteArray(JPEG_APP1_Marker,
+						byteOrder);
+				if (newBytes.length > 0xffff)
+					throw new ExifOverflowException(
+							"APP1 Segment is too long: " + newBytes.length);
+				int markerLength = newBytes.length + 2;
+				byte markerLengthBytes[] = convertShortToByteArray(
+						markerLength, byteOrder);
+
+				int index = 0;
+				JFIFPieceSegment firstSegment = (JFIFPieceSegment) segments
+						.get(index);
+				if (firstSegment.marker == JFIFMarker)
+					index = 1;
+				segments.add(0, new JFIFPieceSegmentExif(JPEG_APP1_Marker,
+						markerBytes, markerLengthBytes, newBytes));
+			}
+
+			boolean APP1Written = false;
+
+			for (int i = 0; i < segments.size(); i++)
+			{
+				JFIFPiece piece = (JFIFPiece) segments.get(i);
+				if (piece instanceof JFIFPieceSegmentExif)
 				{
+					// only replace first APP1 segment; skips others.
+					if (APP1Written)
+						continue;
+					APP1Written = true;
+
+					if (newBytes == null)
+						continue;
+
 					byte markerBytes[] = convertShortToByteArray(
 							JPEG_APP1_Marker, byteOrder);
-					os.write(markerBytes);
-
 					if (newBytes.length > 0xffff)
 						throw new ExifOverflowException(
 								"APP1 Segment is too long: " + newBytes.length);
-
 					int markerLength = newBytes.length + 2;
 					byte markerLengthBytes[] = convertShortToByteArray(
 							markerLength, byteOrder);
-					os.write(markerLengthBytes);
 
+					os.write(markerBytes);
+					os.write(markerLengthBytes);
 					os.write(newBytes);
 				}
 				else
-					throw new ImageWriteException("Unknown data: " + o);
+				{
+					piece.write(os);
+				}
 			}
 		}
 		finally

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App13Segment.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App13Segment.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App13Segment.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App13Segment.java Tue Jan 22 20:44:55 2008
@@ -45,10 +45,6 @@
 		super(marker, marker_length, is);
 		this.parser = parser;
 
-		//			InputStream bais = new ByteArrayInputStream(bytes);
-		//			dump();
-
-		//		Debug.debug("bytes", bytes);
 		boolean verbose = false;
 
 		if (!compareByteArrays(bytes, 0,
@@ -72,7 +68,7 @@
 			int segmentType = convertByteArrayToShort("SegmentType", index,
 					bytes);
 			if (verbose)
-				debugNumber("SegmentType", segmentType, 2);
+				debugNumber("segmentType", segmentType, 2);
 			index += 2;
 
 			// padding
@@ -81,21 +77,19 @@
 			int segmentSize = convertByteArrayToShort("SegmentSize", index,
 					bytes);
 			if (verbose)
-				debugNumber("fSegmentSize", segmentSize, 2);
+				debugNumber("segmentSize", segmentSize, 2);
 			index += 2;
 
 			int index2 = index;
 
 			index += segmentSize;
-			if ((segmentSize % 2) != 0)
-				index++;
 
-			while (index2 < index)
+			while (index2 + 1 < index)
 			{
 				int iptcPrefix = convertByteArrayToShort("IPTCPrefix", index2,
 						bytes);
 				if (verbose)
-					debugNumber("fIPTCPrefix", iptcPrefix, 2);
+					debugNumber("IPTCPrefix", iptcPrefix, 2);
 				index2 += 2;
 
 				if (iptcPrefix == 0x1c02)
@@ -131,6 +125,8 @@
 				elements.add(element);
 			}
 
+			if ((segmentSize % 2) != 0)
+				index++;
 		}
 
 	}

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App2Segment.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App2Segment.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App2Segment.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/App2Segment.java Tue Jan 22 20:44:55 2008
@@ -57,7 +57,7 @@
 		}
 		else
 		{
-			debugByteArray("Unknown APP2 Segment Type", bytes);
+			//			debugByteArray("Unknown APP2 Segment Type", bytes);
 			cur_marker = -1;
 			num_markers = -1;
 			icc_bytes = null;

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/JFIFSegment.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/JFIFSegment.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/JFIFSegment.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/JFIFSegment.java Tue Jan 22 20:44:55 2008
@@ -23,7 +23,7 @@
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.formats.jpeg.JpegConstants;
 
-public class JFIFSegment extends Segment
+public class JFIFSegment extends Segment implements JpegConstants
 {
 	public final int jfifMajorVersion;
 	public final int jfifMinorVersion;
@@ -51,8 +51,11 @@
 	{
 		super(marker, marker_length);
 
-		readAndVerifyBytes(is, JpegConstants.JFIF0_SIGNATURE,
-				"Not a Valid JPEG File: missing JFIF string");
+		byte signature[] = readBytes(is, JFIF0_SIGNATURE.length);
+		if (!compareByteArrays(signature, JFIF0_SIGNATURE)
+				&& !compareByteArrays(signature, JFIF0_SIGNATURE_ALTERNATIVE))
+			throw new ImageReadException(
+					"Not a Valid JPEG File: missing JFIF string");
 
 		jfifMajorVersion = readByte("JFIF_major_version", is,
 				"Not a Valid JPEG File");

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java Tue Jan 22 20:44:55 2008
@@ -27,7 +27,7 @@
 {
 	public final int width, height;
 	public final int numberOfComponents;
-	public final int Precision;
+	public final int precision;
 
 	public SOFNSegment(int marker, byte segmentData[])
 			throws ImageReadException, IOException
@@ -44,7 +44,7 @@
 			System.out.println("SOF0Segment marker_length: " + marker_length);
 
 		{
-			Precision = readByte("Data_precision", is, "Not a Valid JPEG File");
+			precision = readByte("Data_precision", is, "Not a Valid JPEG File");
 			height = read2Bytes("Image_height", is, "Not a Valid JPEG File");
 			width = read2Bytes("Image_Width", is, "Not a Valid JPEG File");
 			numberOfComponents = readByte("Number_of_components", is,

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/Segment.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/Segment.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/Segment.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/Segment.java Tue Jan 22 20:44:55 2008
@@ -39,6 +39,11 @@
 
 	public abstract String getDescription();
 
+	public String toString()
+	{
+		return "[Segment: " + getDescription() + "]";
+	}
+
 	public String getSegmentType()
 	{
 

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java Tue Jan 22 20:44:55 2008
@@ -459,7 +459,7 @@
 		}
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		ArrayList chunks = readChunks(byteSource, new int[]{
@@ -746,7 +746,7 @@
 			//					throw new ImageReadException("PNG: unknown bit depth: " + BitDepth);
 			//			}
 
-//			ColorModel cm = null;
+			//			ColorModel cm = null;
 			int samplesPerPixel = samplesPerPixel(pngChunkIHDR.colorType);
 			boolean isGrayscale = isGrayscale(pngChunkIHDR.colorType);
 

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java Tue Jan 22 20:44:55 2008
@@ -94,7 +94,7 @@
 
 	public abstract void drive() throws ImageReadException, IOException;
 
-//	private long count = 0;
+	//	private long count = 0;
 
 	protected int getRGB(BitParser bitParser, int pixel_index_in_scanline)
 			throws ImageReadException, IOException

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java Tue Jan 22 20:44:55 2008
@@ -43,7 +43,7 @@
 			System.arraycopy(bytes, 0, Keyword_bytes, 0, index);
 			Keyword = new String(Keyword_bytes);
 
-//			int CompressionMethod = bytes[index + 1];
+			//			int CompressionMethod = bytes[index + 1];
 
 			int CompressedTextLength = bytes.length - (index + 1 + 1);
 			byte CompressedText[] = new byte[CompressedTextLength];

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pnm/PNMImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pnm/PNMImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pnm/PNMImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pnm/PNMImageParser.java Tue Jan 22 20:44:55 2008
@@ -188,7 +188,7 @@
 		return null;
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		FileInfo info = readHeader(byteSource);

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/psd/PsdImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/psd/PsdImageParser.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/psd/PsdImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/psd/PsdImageParser.java Tue Jan 22 20:44:55 2008
@@ -544,7 +544,7 @@
 		}
 	}
 
-	public ImageInfo getImageInfo(ByteSource byteSource)
+	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
 			throws ImageReadException, IOException
 	{
 		ImageContents imageContents = readImageContents(byteSource);

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffDirectory.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffDirectory.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffDirectory.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffDirectory.java Tue Jan 22 20:44:55 2008
@@ -35,7 +35,7 @@
 		return TiffDirectory.description(type);
 	}
 
-	public String getElementDescription(boolean verbose)
+	public String getElementDescription(boolean verbose) 
 	{
 		if (!verbose)
 			return "TIFF Directory (" + description() + ")";
@@ -188,6 +188,8 @@
 
 		return null;
 	}
+	
+
 
 	public final class ImageDataElement extends TiffElement
 	{

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffField.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffField.java?rev=614420&r1=614419&r2=614420&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffField.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/TiffField.java Tue Jan 22 20:44:55 2008
@@ -21,8 +21,10 @@
 import java.io.PrintWriter;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.sanselan.ImageReadException;
@@ -62,11 +64,19 @@
 		tagInfo = getTag(directoryType, tag);
 	}
 
+	private int sortHint = -1;
+	
+	
 	public boolean isLocalValue()
 	{
 		return fieldType.isLocalValue(this);
 	}
 
+	public int getBytesLength() throws ImageReadException
+	{
+		return fieldType.getBytesLength(this);
+	}
+
 	public final class OversizeValueElement extends TiffElement
 	{
 		public OversizeValueElement(int offset, int length)
@@ -109,29 +119,132 @@
 		return FIELD_TYPE_UNKNOWN;
 	}
 
-	private static TagInfo getTag(int directoryType, int value)
+	private static TagInfo getTag(int directoryType, int tag,
+			List possibleMatches)
 	{
-		Object key = new Integer(value);
+		if (possibleMatches.size() < 1)
+			return null;
+		//		else if (possibleMatches.size() == 1)
+		//		{
+		//			TagInfo tagInfo = (TagInfo) possibleMatches.get(0);
+		//			return tagInfo;
+		//		}
 
-		if (directoryType == DIRECTORY_TYPE_EXIF
-				|| directoryType == DIRECTORY_TYPE_INTEROPERABILITY)
-		{
-			if (EXIF_TAG_MAP.containsKey(key))
-				return (TagInfo) EXIF_TAG_MAP.get(key);
-		}
-		else if (directoryType == DIRECTORY_TYPE_GPS)
+		// first search for exact match.
+		for (int i = 0; i < possibleMatches.size(); i++)
 		{
-			if (GPS_TAG_MAP.containsKey(key))
-				return (TagInfo) GPS_TAG_MAP.get(key);
-		}
-		else
+			TagInfo tagInfo = (TagInfo) possibleMatches.get(i);
+			if (tagInfo.directoryType == EXIF_DIRECTORY_UNKNOWN)
+				// pass
+				continue;
+			else if (directoryType == DIRECTORY_TYPE_EXIF
+					&& tagInfo.directoryType == EXIF_DIRECTORY_EXIF_IFD)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_INTEROPERABILITY
+					&& tagInfo.directoryType == EXIF_DIRECTORY_INTEROP_IFD)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_GPS
+					&& tagInfo.directoryType == EXIF_DIRECTORY_GPS)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_MAKER_NOTES
+					&& tagInfo.directoryType == EXIF_DIRECTORY_MAKER_NOTES)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_DIR_0
+					&& tagInfo.directoryType == TIFF_DIRECTORY_IFD0)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_DIR_1
+					&& tagInfo.directoryType == TIFF_DIRECTORY_IFD1)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_DIR_2
+					&& tagInfo.directoryType == TIFF_DIRECTORY_IFD2)
+				return tagInfo;
+			else if (directoryType == DIRECTORY_TYPE_DIR_3
+					&& tagInfo.directoryType == TIFF_DIRECTORY_IFD3)
+				return tagInfo;
+		}
+
+		// accept an inexact match.
+		for (int i = 0; i < possibleMatches.size(); i++)
+		{
+			TagInfo tagInfo = (TagInfo) possibleMatches.get(i);
+
+			if (tagInfo.directoryType == EXIF_DIRECTORY_UNKNOWN)
+				// pass
+				continue;
+			else if (directoryType >= 0
+					&& tagInfo.directoryType.isImageDirectory())
+				return tagInfo;
+			else if (directoryType < 0
+					&& !tagInfo.directoryType.isImageDirectory())
+				return tagInfo;
+		}
+
+		// accept a wildcard match.
+		for (int i = 0; i < possibleMatches.size(); i++)
 		{
-			if (TIFF_TAG_MAP.containsKey(key))
-				return (TagInfo) TIFF_TAG_MAP.get(key);
+			TagInfo tagInfo = (TagInfo) possibleMatches.get(i);
+
+			if (tagInfo.directoryType == EXIF_DIRECTORY_UNKNOWN)
+				return tagInfo;
 		}
 
-		if (ALL_TAG_MAP.containsKey(key))
-			return (TagInfo) ALL_TAG_MAP.get(key);
+		//		// accept a very rough match.
+		//		for (int i = 0; i < possibleMatches.size(); i++)
+		//		{
+		//			TagInfo tagInfo = (TagInfo) possibleMatches.get(i);
+		//			if (tagInfo.exifDirectory == EXIF_DIRECTORY_UNKNOWN)
+		//				return tagInfo;
+		//			else if (directoryType == DIRECTORY_TYPE_EXIF
+		//					&& tagInfo.exifDirectory == EXIF_DIRECTORY_EXIF_IFD)
+		//				return tagInfo;
+		//			else if (directoryType == DIRECTORY_TYPE_INTEROPERABILITY
+		//					&& tagInfo.exifDirectory == EXIF_DIRECTORY_INTEROP_IFD)
+		//				return tagInfo;
+		//			else if (directoryType == DIRECTORY_TYPE_GPS
+		//					&& tagInfo.exifDirectory == EXIF_DIRECTORY_GPS)
+		//				return tagInfo;
+		//			else if (directoryType == DIRECTORY_TYPE_MAKER_NOTES
+		//					&& tagInfo.exifDirectory == EXIF_DIRECTORY_MAKER_NOTES)
+		//				return tagInfo;
+		//			else if (directoryType >= 0
+		//					&& tagInfo.exifDirectory.isImageDirectory())
+		//				return tagInfo;
+		//			else if (directoryType < 0
+		//					&& !tagInfo.exifDirectory.isImageDirectory())
+		//				return tagInfo;
+		//		}
+
+		return TIFF_TAG_UNKNOWN;
+
+		//		if (true)
+		//			throw new Error("Why didn't this algorithm work?");
+		//
+		//		{
+		//			TagInfo tagInfo = (TagInfo) possibleMatches.get(0);
+		//			return tagInfo;
+		//		}
+
+		//		Object key = new Integer(tag);
+		//
+		//		if (directoryType == DIRECTORY_TYPE_EXIF
+		//				|| directoryType == DIRECTORY_TYPE_INTEROPERABILITY)
+		//		{
+		//			if (EXIF_TAG_MAP.containsKey(key))
+		//				return (TagInfo) EXIF_TAG_MAP.get(key);
+		//		}
+		//		else if (directoryType == DIRECTORY_TYPE_GPS)
+		//		{
+		//			if (GPS_TAG_MAP.containsKey(key))
+		//				return (TagInfo) GPS_TAG_MAP.get(key);
+		//		}
+		//		else
+		//		{
+		//			if (TIFF_TAG_MAP.containsKey(key))
+		//				return (TagInfo) TIFF_TAG_MAP.get(key);
+		//		}
+		//
+		//		if (ALL_TAG_MAP.containsKey(key))
+		//			return (TagInfo) ALL_TAG_MAP.get(key);
 
 		//		public static final int DIRECTORY_TYPE_EXIF = -2;
 		//		//	public static final int DIRECTORY_TYPE_SUB = 5;
@@ -150,7 +263,76 @@
 		//				return tag;
 		//		}
 
-		return TIFF_TAG_UNKNOWN;
+		//		return TIFF_TAG_UNKNOWN;
+	}
+
+	private static TagInfo getTag(int directoryType, int tag)
+	{
+		//		Debug.debug();
+		//		Debug
+		//				.debug("getTag tag", tag + " (0x" + Integer.toHexString(tag)
+		//						+ ")");
+		//		Debug.debug("getTag directoryType", directoryType + " (0x"
+		//				+ Integer.toHexString(directoryType) + ")");
+
+		Object key = new Integer(tag);
+
+		List possibleMatches = (List) EXIF_TAG_MAP.get(key);
+
+		if (null == possibleMatches)
+		{
+//			if (tag == 0x8769)
+//				Debug.debug("exif offset field is unknown.1");
+			return TIFF_TAG_UNKNOWN;
+		}
+
+		TagInfo result = getTag(directoryType, tag, possibleMatches);
+		//		if (tag == 0x8769)
+		//		{
+		//			Debug.debug("exif offset possibleMatches", possibleMatches);
+		//			Debug.debug("exif offset field", result);
+		//		}
+		//		Debug.debug("result", result);
+		return result;
+
+		//		if (directoryType == DIRECTORY_TYPE_EXIF
+		//				|| directoryType == DIRECTORY_TYPE_INTEROPERABILITY)
+		//		{
+		//			if (EXIF_TAG_MAP.containsKey(key))
+		//				return (TagInfo) EXIF_TAG_MAP.get(key);
+		//		}
+		//		else if (directoryType == DIRECTORY_TYPE_GPS)
+		//		{
+		//			if (GPS_TAG_MAP.containsKey(key))
+		//				return (TagInfo) GPS_TAG_MAP.get(key);
+		//		}
+		//		else
+		//		{
+		//			if (TIFF_TAG_MAP.containsKey(key))
+		//				return (TagInfo) TIFF_TAG_MAP.get(key);
+		//		}
+		//
+		//		if (ALL_TAG_MAP.containsKey(key))
+		//			return (TagInfo) ALL_TAG_MAP.get(key);
+		//
+		//		//		public static final int DIRECTORY_TYPE_EXIF = -2;
+		//		//		//	public static final int DIRECTORY_TYPE_SUB = 5;
+		//		//		public static final int DIRECTORY_TYPE_GPS = -3;
+		//		//		public static final int DIRECTORY_TYPE_INTEROPERABILITY = -4;
+		//		//
+		//		//		private static final Map GPS_TAG_MAP = makeTagMap(ALL_GPS_TAGS, false);
+		//		//		private static final Map TIFF_TAG_MAP = makeTagMap(ALL_TIFF_TAGS, false);
+		//		//		private static final Map EXIF_TAG_MAP = makeTagMap(ALL_EXIF_TAGS, false);
+		//		//		private static final Map ALL_TAG_MAP = makeTagMap(ALL_TAGS, true);
+		//		//
+		//		//		for (int i = 0; i < ALL_TAGS.length; i++)
+		//		//		{
+		//		//			TagInfo2 tag = ALL_TAGS[i];
+		//		//			if (tag.tag == value)
+		//		//				return tag;
+		//		//		}
+		//
+		//		return TIFF_TAG_UNKNOWN;
 	}
 
 	private int getValueLengthInBytes()
@@ -168,9 +350,9 @@
 
 		int valueLength = getValueLengthInBytes();
 
-		//				Debug.debug("fillInValue tagInfo", tagInfo);
-		//				Debug.debug("fillInValue valueOffset", valueOffset);
-		//				Debug.debug("fillInValue valueLength", valueLength);
+		//						Debug.debug("fillInValue tagInfo", tagInfo);
+		//						Debug.debug("fillInValue valueOffset", valueOffset);
+		//						Debug.debug("fillInValue valueLength", valueLength);
 
 		byte bytes[] = byteSource.getBlock(valueOffset, valueLength);
 		setOversizeValue(bytes);
@@ -178,7 +360,14 @@
 
 	public String getValueDescription()
 	{
-		return getValueDescription(getValue());
+		try
+		{
+			return getValueDescription(getValue());
+		}
+		catch (ImageReadException e)
+		{
+			return "Invalid value: " + e.getMessage();
+		}
 	}
 
 	private String getValueDescription(Object o)
@@ -189,19 +378,6 @@
 		if (o instanceof Number)
 		{
 			return o.toString();
-			//			try
-			//			{
-			//				return o.toString();
-			//			}
-			//			catch (ArithmeticException e)
-			//			{
-			//				errorDump();
-			//				
-			//				//				Debug.debug(toString());
-			//				//				Debug.debug(e);
-			//
-			//				throw e;
-			//			}
 		}
 		else if (o instanceof String)
 		{
@@ -430,7 +606,7 @@
 
 		result.append(tag + " (0x" + Integer.toHexString(tag) + ": "
 				+ tagInfo.name + "): ");
-		result.append(getValueDescription(getValue()) + " (" + length + " "
+		result.append(getValueDescription() + " (" + length + " "
 				+ fieldType.name + ")");
 
 		return result.toString();
@@ -450,12 +626,23 @@
 
 	public static final String Attribute_Tag = "Tag";
 
-	public Object getValue()
+	public Object getValue() throws ImageReadException
 	{
 		//		System.out.print("getValue");
 		return tagInfo.getValue(this);
 	}
 
+	public String getStringValue() throws ImageReadException
+	{
+		Object o = getValue();
+		if (o == null)
+			return null;
+		if (!(o instanceof String))
+			throw new ImageReadException("Expected String value("
+					+ tagInfo.getDescription() + "): " + o);
+		return (String) o;
+	}
+
 	private static final Map makeTagMap(TagInfo tags[],
 			boolean ignoreDuplicates, String name)
 	{
@@ -467,16 +654,24 @@
 			TagInfo tag = tags[i];
 			Object key = new Integer(tag.tag);
 
-			if (map.get(key) == null)
-				map.put(key, tag);
-			else if (!ignoreDuplicates)
+			List tagList = (List) map.get(key);
+			if (tagList == null)
 			{
-				System.out.println("Duplicate tag in " + name + ": " + tag.tag
-						+ " (0x" + Integer.toHexString(tag.tag) + ")");
-				System.out.println("\t" + "New name: " + tag.name);
-				System.out.println("\t" + "Old name: "
-						+ ((TagInfo) map.get(key)).name);
+				tagList = new ArrayList();
+				map.put(key, tagList);
 			}
+			tagList.add(tag);
+
+			//			if (map.get(key) == null)
+			//				map.put(key, tag);
+			//			else if (!ignoreDuplicates)
+			//			{
+			//				System.out.println("Duplicate tag in " + name + ": " + tag.tag
+			//						+ " (0x" + Integer.toHexString(tag.tag) + ")");
+			//				System.out.println("\t" + "New name: " + tag.name);
+			//				System.out.println("\t" + "Old name: "
+			//						+ ((TagInfo) map.get(key)).name);
+			//			}
 		}
 
 		return map;
@@ -647,5 +842,15 @@
 					+ tagInfo.getDescription());
 
 		return ((Number) o).doubleValue();
+	}
+
+	public int getSortHint()
+	{
+		return sortHint;
+	}
+
+	public void setSortHint(int sortHint)
+	{
+		this.sortHint = sortHint;
 	}
 }