Mercurial > hg > digilib-old
changeset 531:9cedd170b581 digilibPDF
* PDF generation works now even with subdirectories
* genericsification and clean up
line wrap: on
line diff
--- a/servlet/src/digilib/auth/AuthOps.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/auth/AuthOps.java Fri Feb 05 20:58:38 2010 +0100 @@ -20,33 +20,96 @@ package digilib.auth; +import java.util.List; -import java.io.*; -import java.util.*; -import javax.servlet.http.*; +import javax.servlet.http.HttpServletRequest; +import digilib.servlet.DigilibRequest; + +/** Class of operations requiring authentication. */ public interface AuthOps { - /** - * check if the request must be authorized to access filepath - */ - public boolean isAuthRequired(String filepath, HttpServletRequest request) throws AuthOpException; + /** Test if the request must be authorized to access the filepath. + * + * Information about the user is taken from the ServletRequest. + * @param filepath filepath to be accessed. + * @param request ServletRequest with user information. + * @throws AuthOpException Exception thrown on error. + * @return true if the user request must be authorized. + */ + public boolean isAuthRequired(String filepath, HttpServletRequest request) + throws AuthOpException; - /** - * check if the request is allowed to access filepath - */ - public boolean isAuthorized(String filepath, HttpServletRequest request) throws AuthOpException; + /** Test if the request must be authorized to access the filepath. + * + * Information about the user is taken from the DigilibRequest. + * @param request DigilibRequest with user information. + * @throws AuthOpException Exception thrown on error. + * @return true if the user request must be authorized. + */ + public boolean isAuthRequired(DigilibRequest request) + throws AuthOpException; + + /** Test if the request is allowed to access filepath. + * + * @param filepath filepath to be acessed. + * @param request Request with user information. + * @throws AuthOpException Exception thrown on error. + * @return true if the request is allowed. + */ + public boolean isAuthorized(String filepath, HttpServletRequest request) + throws AuthOpException; + + /** Test if the request is allowed to access filepath. + * + * @param request Request with user information. + * @throws AuthOpException Exception thrown on error. + * @return true if the request is allowed. + */ + public boolean isAuthorized(DigilibRequest request) + throws AuthOpException; - /** - * return a list of authorization roles needed for request - * to access the specified path - * (does not look at request address for now) - */ - public List rolesForPath(String filepath, HttpServletRequest request) throws AuthOpException; + /** Authorization roles needed for request. + * + * Returns the list of authorization roles that are needed to access the + * specified path. No list means the path is free. + * + * The location information of the request is also considered. + * + * @param filepath filepath to be accessed. + * @param request ServletRequest with address information. + * @throws AuthOpException Exception thrown on error. + * @return List of Strings with role names. + */ + public List<String> rolesForPath(String filepath, HttpServletRequest request) + throws AuthOpException; - /** - * check request authorization against a list of roles - */ - public boolean isRoleAuthorized(List roles, HttpServletRequest request); + /** Authorization roles needed for request. + * + * Returns the list of authorization roles that are needed to access the + * specified path. No list means the path is free. + * + * The location information of the request is also considered. + * + * @param request DigilibRequest with address information. + * @throws AuthOpException Exception thrown on error. + * @return List of Strings with role names. + */ + public List<String> rolesForPath(DigilibRequest request) + throws AuthOpException; + + /** Test request authorization against a list of roles. + * @param roles List of Strings with role names. + * @param request ServletRequest with address information. + * @return true if the user information in the request authorizes one of the roles. + */ + public boolean isRoleAuthorized(List<String> roles, HttpServletRequest request); + + /** Test request authorization against a list of roles. + * @param roles List of Strings with role names. + * @param request ServletRequest with address information. + * @return true if the user information in the request authorizes one of the roles. + */ + public boolean isRoleAuthorized(List<String> roles, DigilibRequest request); }
--- a/servlet/src/digilib/auth/AuthOpsImpl.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/auth/AuthOpsImpl.java Fri Feb 05 20:58:38 2010 +0100 @@ -20,58 +20,112 @@ package digilib.auth; +import java.util.List; + import javax.servlet.http.HttpServletRequest; -import java.util.*; + +import org.apache.log4j.Logger; -import digilib.*; +import digilib.servlet.DigilibRequest; +/** Basic implementation of AuthOps interface. + * + * Provides basic implementations. Only rolesForPath needs to be implemented + * by specific implementations. + */ public abstract class AuthOpsImpl implements AuthOps { - protected Utils util; - + /** general logger for this class */ + protected Logger logger = Logger.getLogger(this.getClass()); + + /** Default constructor. */ public AuthOpsImpl() { - util = new Utils(); - try { - init(); - } catch (AuthOpException e) { - } - } - - public AuthOpsImpl(Utils u) { - util = u; try { init(); } catch (AuthOpException e) { } } + + /** Test if the request is allowed to access filepath. + * @param filepath filepath to be acessed. + * @param request Request with user information. + * @throws AuthOpException Exception thrown on error. + * @return true if the request is allowed. + */ public boolean isAuthRequired(String filepath, HttpServletRequest request) throws AuthOpException { // check permissions - List rolesRequired = rolesForPath(filepath, request); + List<String> rolesRequired = rolesForPath(filepath, request); return (rolesRequired != null); } + /** + * @see digilib.auth.AuthOps#isAuthRequired(digilib.servlet.DigilibRequest) + */ + public boolean isAuthRequired(DigilibRequest request) + throws AuthOpException { + // check permissions + List<String> rolesRequired = rolesForPath(request); + return (rolesRequired != null); + } + + /** Return authorization roles needed for request. + * + * Returns a list of authorization roles that would be allowed to access the + * specified path. The location information of the request is considered also. + * @param filepath filepath to be accessed. + * @param request ServletRequest with address information. + * @throws AuthOpException Exception thrown on error. + * @return List of Strings with role names. + */ public boolean isAuthorized(String filepath, HttpServletRequest request) throws AuthOpException { - List rolesAllowed = rolesForPath(filepath, request); + List<String> rolesAllowed = rolesForPath(filepath, request); return isRoleAuthorized(rolesAllowed, request); } - public boolean isRoleAuthorized(List roles, HttpServletRequest request) { - ListIterator r = roles.listIterator(); - String s = ""; - while (r.hasNext()) { - s = (String)r.next(); - util.dprintln(5, "Testing role: "+s); + /** + * @see digilib.auth.AuthOps#isAuthorized(digilib.servlet.DigilibRequest) + */ + public boolean isAuthorized(DigilibRequest request) + throws AuthOpException { + List<String> rolesAllowed = rolesForPath(request); + return isRoleAuthorized(rolesAllowed, request); + } + + /** Test request authorization against a list of roles. + * @param roles List of Strings with role names. + * @param request ServletRequest with address information. + * @return true if the user information in the request authorizes one of the roles. + */ + public boolean isRoleAuthorized(List<String> roles, HttpServletRequest request) { + for (String s: roles) { + logger.debug("Testing role: "+s); if (request.isUserInRole(s)) { - util.dprintln(5, "Role Authorized"); + logger.debug("Role Authorized"); return true; } } return false; } + /** + * @see digilib.auth.AuthOps#isRoleAuthorized(java.util.List, digilib.servlet.DigilibRequest) + */ + public boolean isRoleAuthorized(List<String> roles, DigilibRequest request) { + for (String s: roles) { + logger.debug("Testing role: "+s); + if (((HttpServletRequest)request.getServletRequest()).isUserInRole(s)) { + logger.debug("Role Authorized"); + return true; + } + } + return false; + } + public abstract void init() throws AuthOpException; - public abstract List rolesForPath(String filepath, HttpServletRequest request) throws AuthOpException; + public abstract List<String> rolesForPath(String filepath, HttpServletRequest request) throws AuthOpException; + + public abstract List<String> rolesForPath(DigilibRequest request) throws AuthOpException; }
--- a/servlet/src/digilib/auth/HashTree.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/auth/HashTree.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,80 +1,113 @@ /* HashTree -- Tree in a Hashtable - Digital Image Library servlet components + Digital Image Library servlet components - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ + */ package digilib.auth; -import java.util.*; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +/** + * Tree representation wrapper for a HashMap. + * + * The HashTree is constructed from a HashMap filled with 'branches' with + * 'leaves'. The branches are stored as String keys in the HashMap. The String + * values are leaves. + * + * Branches are matched in 'twigs' separated by 'twig separator' Strings. The + * return values for a match are leaf values separated by 'leaf separator' + * Strings. + * + * @author casties + */ public class HashTree { - private Hashtable table; - private String twigSep = "/"; - private String leafSep = ","; + private Map<String, String> table; - public HashTree() { - table = new Hashtable(); - } + private String twigSep = "/"; + + private String leafSep = ","; - public HashTree(Hashtable t, String twig_separator, String leaf_separator) { - table = t; - twigSep = twig_separator; - leafSep = leaf_separator; - optimizeTable(); - } + /** + * Constructor of a HashTree. + * + * Creates a HashTree wrapper around a given HashMap, using the given twig + * separator and leaf separator. + * + * @param t + * @param twig_separator + * @param leaf_separator + */ + public HashTree(Map<String, String> t, String twig_separator, String leaf_separator) { + table = t; + twigSep = twig_separator; + leafSep = leaf_separator; + optimizeTable(); + } - void optimizeTable() { - } - - List match(String branch) { - String b = ""; - String m; - LinkedList matches = new LinkedList(); + void optimizeTable() { + } - // split branch - StringTokenizer twig = new StringTokenizer(branch, twigSep); - // walk branch and check with tree - while (twig.hasMoreTokens()) { - if (b.length() == 0) { - b = twig.nextToken(); - } else { - b += twigSep + twig.nextToken(); - } - m = (String)table.get(b); - //System.out.println("CHECK: "+b+" = "+m); - if (m != null) { - if (m.indexOf(leafSep) < 0) { - // single leaf - matches.add(m); + /** + * Matches the given branch against the HashTree. + * + * Returns a LinkedList of all leaves on all matching branches in the tree. + * Branches in the tree match if they are substrings starting at the same + * root. + * + * @param branch + * @return + */ + List<String> match(String branch) { + String b = ""; + String m; + LinkedList<String> matches = new LinkedList<String>(); + + // split branch + StringTokenizer twig = new StringTokenizer(branch, twigSep); + // walk branch and check with tree + while (twig.hasMoreTokens()) { + if (b.length() == 0) { + b = twig.nextToken(); + } else { + b += twigSep + twig.nextToken(); + } + m = table.get(b); + if (m != null) { + if (m.indexOf(leafSep) < 0) { + // single leaf + matches.add(m); + } else { + // split leaves + StringTokenizer leaf = new StringTokenizer(m, leafSep); + while (leaf.hasMoreTokens()) { + matches.add(leaf.nextToken()); + } + } + } + } + if (matches.size() > 0) { + return matches; } else { - // split leaves - StringTokenizer leaf = new StringTokenizer(m, leafSep); - while (leaf.hasMoreTokens()) { - matches.add(leaf.nextToken()); - } + return null; } - } } - if (matches.size() > 0) { - return matches; - } else { - return null; - } - } }
--- a/servlet/src/digilib/auth/XMLAuthOps.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/auth/XMLAuthOps.java Fri Feb 05 20:58:38 2010 +0100 @@ -20,78 +20,148 @@ package digilib.auth; +import java.io.File; +import java.util.List; +import java.util.Map; + import javax.servlet.http.HttpServletRequest; -import java.util.*; -import java.io.*; -import digilib.*; -import digilib.io.*; +import digilib.io.XMLListLoader; +import digilib.servlet.DigilibRequest; - +/** Implementation of AuthOps using XML files. + * + * The configuration file is read by an XMLListLoader into HashTree objects for + * authentication paths and IP numbers. + */ public class XMLAuthOps extends AuthOpsImpl { - private String configFile = "/docuserver/www/digitallibrary/WEB-INF/digilib-auth.xml"; - private HashTree authPaths; - private HashTree authIPs; + private File configFile; + private HashTree authPaths; + private HashTree authIPs; + + /** Constructor taking an XML config file. + * + * @param u utils object + * @param confFile Configuration file. + * @throws AuthOpException Exception thrown on error. + */ + public XMLAuthOps(File confFile) throws AuthOpException { + configFile = confFile; + init(); + } + + /** Set configuration file. + * + * @param confFile XML config file. + * @throws AuthOpException Exception thrown on error. + */ + public void setConfig(File confFile) throws AuthOpException { + configFile = confFile; + init(); + } - public XMLAuthOps(Utils u, String confFile) throws AuthOpException { - util = u; - configFile = confFile; - init(); - } - - public void setConfig(String confFile) throws AuthOpException { - configFile = confFile; - init(); - } + /** Initialize. + * + * Read configuration files and setup authentication arrays. + * + * @throws AuthOpException Exception thrown on error. + */ + public void init() throws AuthOpException { + logger.debug("xmlauthops.init (" + configFile + ")"); + Map<String, String> pathList = null; + Map<String, String> ipList = null; + try { + // load authPaths + XMLListLoader pathLoader = + new XMLListLoader("digilib-paths", "path", "name", "role"); + pathList = pathLoader.loadURL(configFile.toURL().toString()); + // load authIPs + XMLListLoader ipLoader = + new XMLListLoader("digilib-addresses", "address", "ip", "role"); + ipList = ipLoader.loadURL(configFile.toURL().toString()); + } catch (Exception e) { + throw new AuthOpException( + "ERROR loading authorization config file: " + e); + } + if ((pathList == null) || (ipList == null)) { + throw new AuthOpException("ERROR unable to load authorization config file!"); + } + // setup path tree + authPaths = new HashTree(pathList, "/", ","); + // setup ip tree + authIPs = new HashTree(ipList, ".", ","); + } - public void init() throws AuthOpException { - util.dprintln(10, "xmlauthops.init ("+configFile+")"); - Hashtable pathList = null; - Hashtable ipList = null; - try { - // create data loader for auth-path file - File confFile = new File(configFile); - // load authPaths - XMLListLoader pathLoader = new XMLListLoader("digilib-paths", "path", "name", "role"); - pathList = pathLoader.loadURL(confFile.toURL().toString()); - // load authIPs - XMLListLoader ipLoader = new XMLListLoader("digilib-addresses", "address", "ip", "role"); - ipList = ipLoader.loadURL(confFile.toURL().toString()); - } - catch (Exception e) { - throw new AuthOpException("ERROR loading authorization config file: "+e); - } - if ((pathList == null)||(ipList == null)) { - throw new AuthOpException("ERROR unable to load authorization config file!"); - } - // setup path tree - authPaths = new HashTree(pathList, "/", ","); - // setup ip tree - authIPs = new HashTree(ipList, ".", ","); - } + /** Return authorization roles needed for request. + * + * Returns the list of authorization roles that are needed to access the + * specified path. No list means the path is free. + * + * The location information of the request is also considered. + * + * @param filepath filepath to be accessed. + * @param request ServletRequest with address information. + * @throws AuthOpException Exception thrown on error. + * @return List of Strings with role names. + */ + public List<String> rolesForPath(String filepath, HttpServletRequest request) + throws digilib.auth.AuthOpException { + logger.debug("rolesForPath (" + + filepath + + ") by [" + + request.getRemoteAddr() + + "]"); - public List rolesForPath(String filepath, HttpServletRequest request) throws digilib.auth.AuthOpException { - util.dprintln(4, "rolesForPath ("+filepath+") by ["+request.getRemoteAddr()+"]"); + // check if the requests address provides a role + List<String> provided = authIPs.match(request.getRemoteAddr()); + if ((provided != null) && (provided.contains("ALL"))) { + // ALL switches off checking; + return null; + } + // which roles are required? + List<String> required = authPaths.match(filepath); + // do any provided roles match? + if ((provided != null) && (required != null)) { + for (int i = 0; i < provided.size(); i++) { + if (required.contains(provided.get(i))) { + // satisfied + return null; + } + } + } + return required; + } - // check if the requests address provides a role - List provided = authIPs.match(request.getRemoteAddr()); - if ((provided != null)&&(provided.contains("ALL"))) { - // ALL switches off checking; - return null; - } - // which roles are required? - List required = authPaths.match(filepath); - // do any provided roles match? - if ((provided != null)&&(required != null)) { - for (int i = 0; i < provided.size(); i++) { - if (required.contains(provided.get(i))) { - // satisfied - return null; - } - } - } - return required; - } + /** + * @see digilib.auth.AuthOps#rolesForPath(digilib.servlet.DigilibRequest) + */ + public List<String> rolesForPath(DigilibRequest request) throws AuthOpException { + logger.debug("rolesForPath (" + + request.getFilePath() + + ") by [" + + request.getServletRequest().getRemoteAddr() + + "]"); + + // check if the requests address provides a role + List<String> provided = + authIPs.match(request.getServletRequest().getRemoteAddr()); + if ((provided != null) && (provided.contains("ALL"))) { + // ALL switches off checking; + return null; + } + // which roles are required? + List<String> required = authPaths.match(request.getFilePath()); + // do any provided roles match? + if ((provided != null) && (required != null)) { + for (int i = 0; i < provided.size(); i++) { + if (required.contains(provided.get(i))) { + // satisfied + return null; + } + } + } + return required; + } }
--- a/servlet/src/digilib/image/DocuImage.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/image/DocuImage.java Fri Feb 05 20:58:38 2010 +0100 @@ -2,7 +2,7 @@ Digital Image Library servlet components - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -14,55 +14,211 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package digilib.image; -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; +import java.awt.Rectangle; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Iterator; + +import digilib.io.ImageFile; +import digilib.io.FileOpException; -import java.awt.image.*; -import java.awt.image.renderable.*; - -import digilib.*; -import digilib.io.*; - +/** The basic class for the representation of a digilib image. + * + * The actual image object is hidden in the class, only methods for loading, + * manipulation, and saving are exported. This strategy enables implementations + * using different toolkits that rely on different image base classes (like + * JIMI, Java2D and JAI). + */ public interface DocuImage { - public String[] getKnownFileTypes(); + /** Loads an image file into the Object. + * + * @param f Image File. + * @throws FileOpException Exception thrown if any error occurs. + */ + public void loadImage(ImageFile f) throws FileOpException; + + /** This DocuImage support the loadSubImage operation. + * + * @return boolean + */ + public boolean isSubimageSupported(); - /** - * send an image file as-is - */ - public void sendFile(File f, ServletResponse res) throws FileOpException; + /** Load only a subsampled region of the image file. + * + * @param f + * @param region + * @param subsample + * @throws FileOpException + */ + public void loadSubimage(ImageFile f, Rectangle region, int subsample) + throws FileOpException; + + /** Writes the current image to a ServletResponse. + * + * The image is encoded to the mime-type <code>mt</code> and sent to the output + * stream of the <code>ServletResponse</code> <code>res</code>. + * + * Currently only mime-types "image/jpeg" and "image/png" are supported. + * + * @param mt mime-type of the image to be sent. + * @param res ServletResponse where the image is sent. + * @throws FileOpException Exception thrown on any error. + */ + public void writeImage(String mt, OutputStream ostream) + throws FileOpException; - /** - * load image file - */ - public void loadImage(File f) throws FileOpException; + /** The width of the current image in pixel. + * + * @return Image width in pixels. + */ + public int getWidth(); + + /** The height of the current image in pixel. + * + * @return Image height in pixels. + */ + public int getHeight(); + + /** The mime-type of the current image. + * + * @return String the mime-type of this image. + */ + public String getMimetype(); - /** - * write image with mime type mt to Stream - */ - public void writeImage(String mt, ServletResponse res) throws FileOpException; + /** Crops the current image. + * + * Cuts out a region of the size <code>width</code> x <code>height</code> at + * the offset <code>xoff</code>, <code>yoff</code> from the current image + * and replaces the current image with the result. + * + * @param xoff X offset of crop region + * @param yoff Y offset of crop region + * @param width width of crop region + * @param height height of crop region + * @throws ImageOpException + */ + public void crop(int xoff, int yoff, int width, int height) + throws ImageOpException; + + /** Scales the current image. + * + * Replaces the current image with an image scaled by the factor + * <code>scale</code>. + * + * @param scale scaling factor + * @throws ImageOpException + */ + public void scale(double scaleX, double scaleY) throws ImageOpException; - /** - * get the image height and width - */ - public int getWidth(); - public int getHeight(); + /** Crops and scales the current image. + * + * The current image is cropped to a rectangle of <code>width</code>, + * <code>height</code> at position <code>x_off</code>, <code>y_off</code>. The + * resulting image is scaled by the factor <code>scale</code> using the + * interpolation quality <code>qual</code> (0=worst). + * + * @param x_off x offset of the crop rectangle in pixel. + * @param y_off y offset of the crop rectangle in pixel. + * @param width width of the crop rectangle in pixel. + * @param height height of the crop rectangle in pixel. + * @param scale scaling factor. + * @param qual interpolation quality (0=worst). + * @throws ImageOpException exception thrown on any error. + */ + public void cropAndScale( + int x_off, + int y_off, + int width, + int height, + double scale, + int qual) + throws ImageOpException; + + /** Rotates the current image. + * + * Replaces the current image with a rotated image. The image is rotated + * around the center by the <code>angle</code> + * given in degrees [0, 360] clockwise. + * Image size and aspect ratio are likely to change. + * + * @param angle rotation angle in degree + */ + public void rotate(double angle) throws ImageOpException; + + /** Mirrors the current image. + * + * Replaces the current image with a mirrored image. The mirror axis goes + * through the center of the image and is rotated by <code>angle</code> + * degrees. Currently only horizontal and vertical mirroring (0 and 90 + * degree) are supported. + * + * @param angle angle of mirror axis + * @throws ImageOpException + */ + public void mirror(double angle) throws ImageOpException; - /** - * crop and scale image - * take rectangle width,height at position x_off,y_off - * and scale by scale with interpolation quality qual (0=worst) - */ - public void cropAndScale( - int x_off, int y_off, - int width, int height, - float scale, int qual) throws ImageOpException; + /** Enhances brightness and contrast of the current image. + * + * Replaces the current image with a brightness and contrast enhanced image. + * Contrast is enhanced by multiplying the pixel value with the constant + * <code>mult</code>. Brightness is enhanced by adding the constant + * <code>add</code> to the pixel value. Operation: p1 = (p0*mult)+add. + * + * @param mult multiplicative constant for contrast enhancement + * @param add additive constant for brightness enhancement + * @throws ImageOpException + */ + public void enhance(float mult, float add) throws ImageOpException; + + /** Manipulates the colors of the current image. + * + * Replaces the current image with a color modified image. + * For the red, green and blue color channels all pixel values are multiplied + * by the constant + * <code>m</code> and added to the constant + * <code>a</code>. Operation: p1 = (p0*m)+a. + * + * @param rgbm multiplicative constants for red, green, blue + * @param rgba additive constant for red, green, blue + * @throws ImageOpException + */ + public void enhanceRGB(float[] rgbm, float[] rgba) + throws ImageOpException; + + /** + * Returns the interpolation quality. + * @return int + */ + public int getQuality(); + + /** + * Sets the interpolation quality. + * @param quality The quality to set + */ + public void setQuality(int quality); + + /** Frees all resources bound to the DocuImage. + * + * Things that should be freed are image objects and open files. + * + */ + public void dispose(); + + /** + * Check image size and type and store in ImageFile f + */ + public boolean identify(ImageFile imgf) throws IOException; + + /** + * Returns a list of supported image formats + */ + public Iterator<String> getSupportedFormats(); + }
--- a/servlet/src/digilib/image/DocuImageImpl.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/image/DocuImageImpl.java Fri Feb 05 20:58:38 2010 +0100 @@ -2,7 +2,7 @@ Digital Image Library servlet components - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -14,71 +14,153 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package digilib.image; -import java.io.*; -import javax.servlet.ServletResponse; +import java.awt.Rectangle; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.marcoschmidt.image.ImageInfo; -import digilib.*; -import digilib.io.*; +import digilib.io.FileOpException; +import digilib.io.ImageFile; +/** Simple abstract implementation of the <code>DocuImage</code> interface. + * + * This implementation provides basic functionality for the utility methods like + * <code>SetUtils</code>, and <code>getKnownFileTypes</code>. Image methods like + * <code>loadImage</code>, <code>writeImage</code>, <code>getWidth</code>, + * <code>getHeight</code>, <code>crop</code> and <code>scale</code> must be + * implemented by derived classes. + */ public abstract class DocuImageImpl implements DocuImage { - protected Utils util = null; - - public DocuImageImpl() { - util = new Utils(); - } - - public DocuImageImpl(Utils u) { - util = u; - } + /** logger */ + protected static final Logger logger = Logger.getLogger(DocuImage.class); + + /** Interpolation quality. */ + protected int quality = 0; + + /** epsilon for float comparisons. */ + public final double epsilon = 1e-5; + + /** image mime-type */ + protected String mimeType = null; - public void setUtils(Utils u) { - util = u; - } + /** + * Returns the quality. + * @return int + */ + public int getQuality() { + return quality; + } - protected String[] knownFileTypes = {"jpg", "png", "gif", "tiff"}; - - public String[] getKnownFileTypes() { - return knownFileTypes; - } + /** + * Sets the quality. + * @param quality The quality to set + */ + public void setQuality(int quality) { + this.quality = quality; + } - /** - * send an image file as-is - */ - public void sendFile(File f, ServletResponse response) throws FileOpException { - util.dprintln(4, "sendFile("+f+")"); - String mimeType = FileOps.mimeForFile(f); - if (mimeType == null) { - util.dprintln(2, "ERROR(sendFile): unknown file Type"); - throw new FileOpException("Unknown file type."); + /** Check image size and type and store in ImageFile f */ + public boolean identify(ImageFile imgf) throws IOException { + // fileset to store the information + File f = imgf.getFile(); + if (f == null) { + throw new IOException("File not found!"); + } + RandomAccessFile raf = new RandomAccessFile(f, "r"); + // set up ImageInfo object + ImageInfo iif = new ImageInfo(); + iif.setInput(raf); + iif.setCollectComments(false); + iif.setDetermineImageNumber(false); + logger.debug("identifying (ImageInfo) " + f); + // try with ImageInfo first + if (iif.check()) { + ImageSize d = new ImageSize(iif.getWidth(), iif.getHeight()); + imgf.setSize(d); + imgf.setMimetype(iif.getMimeType()); + //logger.debug(" format:"+iif.getFormatName()); + raf.close(); + logger.debug("image size: " + imgf.getSize()); + return true; + } + return false; } - response.setContentType(mimeType); - // open file - try { - FileInputStream inFile = new FileInputStream(f); - OutputStream outStream = response.getOutputStream(); - byte dataBuffer[] = new byte[1024]; - int len; - while ((len = inFile.read(dataBuffer)) != -1) { - // copy out file - outStream.write(dataBuffer, 0, len); - } - inFile.close(); - } catch (IOException e) { - util.dprintln(2, "ERROR(sendFile): unable to send file"); - throw new FileOpException("Unable to send file."); - } - } + + /** Crop and scale the current image. + * + * The current image is cropped to a rectangle of width, height at position + * x_off, y_off. The resulting image is scaled by the factor scale using the + * interpolation quality qual (0=worst). + * + * @param x_off X offset of the crop rectangle in pixel. + * @param y_off Y offset of the crop rectangle in pixel. + * @param width Width of the crop rectangle in pixel. + * @param height Height of the crop rectangle in pixel. + * @param scale Scaling factor. + * @param qual Interpolation quality (0=worst). + * @throws ImageOpException Exception thrown on any error. + */ + public void cropAndScale( + int x_off, int y_off, int width, int height, double scale, int qual) + throws ImageOpException { + + setQuality(qual); + crop(x_off, y_off, width, height); + scale(scale, scale); + } + + public String getMimetype() { + return mimeType; + } + + public void rotate(double angle) throws ImageOpException { + // just a do-nothing implementation + } - public abstract void loadImage(File f) throws FileOpException; - public abstract void writeImage(String mt, ServletResponse res) throws FileOpException; - public abstract int getWidth(); - public abstract int getHeight(); - public abstract void cropAndScale(int x_off, int y_off, int width, int height, float scale, int qual) throws ImageOpException; + public void mirror(double angle) throws ImageOpException { + // just a do-nothing implementation + } + + public void enhance(float mult, float add) throws ImageOpException { + // just a do-nothing implementation + } + + public boolean isSubimageSupported() { + // partial loading not supported per default + return false; + } + + public void loadSubimage(ImageFile f, Rectangle region, int subsample) + throws FileOpException { + // empty implementation + } + + public void enhanceRGB(float[] rgbm, float[] rgba) + throws ImageOpException { + // emtpy implementation + } + + public void dispose() { + // emtpy implementation + } + + public Iterator<String> getSupportedFormats() { + List<String> empty = new LinkedList<String>(); + return empty.iterator(); + } + + }
--- a/servlet/src/digilib/image/ImageLoaderDocuImage.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/image/ImageLoaderDocuImage.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,164 +1,570 @@ /* ImageLoaderDocuImage -- Image class implementation using JDK 1.4 ImageLoader - Digital Image Library servlet components + Digital Image Library servlet components - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + Copyright (C) 2002, 2003 Robert Casties (robcast@mail.berlios.de) - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ package digilib.image; -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; +import java.awt.image.RescaleOp; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Iterator; -import java.awt.*; -import java.awt.image.*; -import java.awt.geom.*; -import java.awt.image.renderable.*; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; -import javax.imageio.*; +import digilib.io.FileOpException; +import digilib.io.FileOps; +import digilib.io.ImageFile; +import digilib.io.ImageFileset; -import digilib.*; -import digilib.io.*; - +/** Implementation of DocuImage using the ImageLoader API of Java 1.4 and Java2D. */ public class ImageLoaderDocuImage extends DocuImageImpl { - private BufferedImage img; + /** image object */ + protected BufferedImage img; + + /** interpolation type */ + protected RenderingHints renderHint; - public ImageLoaderDocuImage() { - } + /** ImageIO image reader */ + protected ImageReader reader; - public ImageLoaderDocuImage(Utils u) { - util = u; - } + /** File that was read */ + protected File imgFile; + + /* loadSubimage is supported. */ + public boolean isSubimageSupported() { + return true; + } - /** - * load image file - */ - public void loadImage(File f) throws FileOpException { - util.dprintln(10, "loadImage!"); - System.gc(); - try { - for (int i = 0; i < ImageIO.getReaderFormatNames().length; i++) { - System.out.println("ImageLoader reader:"+ImageIO.getReaderFormatNames()[i]); - } - for (int i = 0; i < ImageIO.getWriterFormatNames().length; i++) { - System.out.println("ImageLoader writer:"+ImageIO.getWriterFormatNames()[i]); - } - img = ImageIO.read(f); - if (img == null) { - util.dprintln(3, "ERROR(loadImage): unable to load file"); - throw new FileOpException("Unable to load File!"); - } - } - catch (IOException e) { - throw new FileOpException("Error reading image."); - } - } + public void setQuality(int qual) { + quality = qual; + renderHint = new RenderingHints(null); + // hint.put(RenderingHints.KEY_ANTIALIASING, + // RenderingHints.VALUE_ANTIALIAS_OFF); + // setup interpolation quality + if (qual > 0) { + logger.debug("quality q1"); + renderHint.put(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + } else { + logger.debug("quality q0"); + renderHint.put(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } + } + + public int getHeight() { + int h = 0; + try { + if (img == null) { + h = reader.getHeight(0); + } else { + h = img.getHeight(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return h; + } - /** - * write image of type mt to Stream - */ - public void writeImage(String mt, ServletResponse res) - throws FileOpException { - util.dprintln(10, "writeImage!"); - try { - // setup output - String type = "png"; - if (mt == "image/jpeg") { - type = "jpeg"; - } else if (mt == "image/png") { - type = "png"; - } else { - // unknown mime type - util.dprintln(2, "ERROR(writeImage): Unknown mime type "+mt); - throw new FileOpException("Unknown mime type: "+mt); - } - res.setContentType(mt); - // render output - if (ImageIO.write(img, type, res.getOutputStream())) { - // writing was OK - return; - } else { - throw new FileOpException("Error writing image: Unknown image format!"); - } - } catch (IOException e) { - // e.printStackTrace(); - throw new FileOpException("Error writing image."); - } - } + public int getWidth() { + int w = 0; + try { + if (img == null) { + w = reader.getWidth(0); + } else { + w = img.getWidth(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return w; + } + + /* returns a list of supported image formats */ + public Iterator<String> getSupportedFormats() { + String[] formats = ImageIO.getReaderFormatNames(); + return Arrays.asList(formats).iterator(); + } - public int getWidth() { - if (img != null) { - return img.getWidth(); - } - return 0; - } - - public int getHeight() { - if (img != null) { - return img.getHeight(); - } - return 0; - } - - /** - * crop and scale image - * take rectangle width,height at position x_off,y_off - * and scale by scale - */ - public void cropAndScale(int x_off, int y_off, int width, int height, - float scale, int qual) throws ImageOpException { - util.dprintln(10, "cropAndScale!"); - - int scaleInt = 0; - // setup interpolation quality - if (qual > 0) { - util.dprintln(4, "quality q1"); - scaleInt = AffineTransformOp.TYPE_BILINEAR; - } else { - util.dprintln(4, "quality q0"); - scaleInt = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; + /** Check image size and type and store in ImageFile f */ + public boolean identify(ImageFile imgf) throws IOException { + // try parent method first + if (super.identify(imgf)) { + return true; + } + // fileset to store the information + ImageFileset imgfs = imgf.getParent(); + File f = imgf.getFile(); + if (f == null) { + throw new IOException("File not found!"); + } + logger.debug("identifying (ImageIO) " + f); + /* + * try ImageReader + */ + RandomAccessFile raf = new RandomAccessFile(f, "r"); + ImageInputStream istream = ImageIO.createImageInputStream(raf); + Iterator<ImageReader> readers = ImageIO.getImageReaders(istream); + if (readers.hasNext()) { + ImageReader reader = readers.next(); + /* are there more readers? */ + logger.debug("ImageIO: this reader: " + reader.getClass()); + while (readers.hasNext()) { + logger.debug("ImageIO: next reader: " + + readers.next().getClass()); + } + try { + reader.setInput(istream); + ImageSize d = new ImageSize(reader.getWidth(0), reader.getHeight(0)); + imgf.setSize(d); + //String t = reader.getFormatName(); + String t = FileOps.mimeForFile(f); + imgf.setMimetype(t); + //logger.debug(" format:"+t); + if (imgfs != null) { + imgfs.setAspect(d); + } + return true; + } finally { + // dispose the reader to free resources + reader.dispose(); + raf.close(); + } + } + throw new FileOpException("ERROR: unknown image file format!"); } - // setup Crop - BufferedImage croppedImg = img.getSubimage(x_off, y_off, width, height); + + /* load image file */ + public void loadImage(ImageFile f) throws FileOpException { + logger.debug("loadImage " + f.getFile()); + try { + img = ImageIO.read(f.getFile()); + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } catch (IOException e) { + throw new FileOpException("Error reading image."); + } + } + + /** + * Get an ImageReader for the image file. + * + * @return + */ + public ImageReader getReader(ImageFile f) throws IOException { + logger.debug("preloadImage " + f.getFile()); + if (reader != null) { + logger.debug("Reader was not null!"); + // clean up old reader + dispose(); + } + // System.gc(); + RandomAccessFile rf = new RandomAccessFile(f.getFile(), "r"); + ImageInputStream istream = new FileImageInputStream(rf); + // Iterator readers = ImageIO.getImageReaders(istream); + String mt = f.getMimetype(); + logger.debug("File type:" + mt); + Iterator<ImageReader> readers = ImageIO.getImageReadersByMIMEType(mt); + if (!readers.hasNext()) { + throw new FileOpException("Unable to load File!"); + } + reader = readers.next(); + /* are there more readers? */ + logger.debug("ImageIO: this reader: " + reader.getClass()); + while (readers.hasNext()) { + logger.debug("ImageIO: next reader: " + readers.next().getClass()); + } + // */ + reader.setInput(istream); + imgFile = f.getFile(); + return reader; + } + + /* Load an image file into the Object. */ + public void loadSubimage(ImageFile f, Rectangle region, int prescale) + throws FileOpException { + logger.debug("loadSubimage"); + // System.gc(); + try { + if ((reader == null) || (imgFile != f.getFile())) { + getReader(f); + } + // set up reader parameters + ImageReadParam readParam = reader.getDefaultReadParam(); + readParam.setSourceRegion(region); + if (prescale > 1) { + readParam.setSourceSubsampling(prescale, prescale, 0, 0); + } + // read image + logger.debug("loading.."); + img = reader.read(0, readParam); + logger.debug("loaded"); + } catch (IOException e) { + throw new FileOpException("Unable to load File!"); + } + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } - img = null; // free img - util.dprintln(3, "CROP:"+croppedImg.getWidth()+"x"+croppedImg.getHeight()); //DEBUG -// util.dprintln(2, " time "+(System.currentTimeMillis()-startTime)+"ms"); + /* write image of type mt to Stream */ + public void writeImage(String mt, OutputStream ostream) + throws FileOpException { + logger.debug("writeImage"); + // setup output + ImageWriter writer = null; + ImageOutputStream imgout = null; + try { + imgout = ImageIO.createImageOutputStream(ostream); + if (mt == "image/jpeg") { + /* + * JPEG doesn't do transparency so we have to convert any RGBA + * image to RGB :-( *Java2D BUG* + */ + if (img.getColorModel().hasAlpha()) { + logger.debug("BARF: JPEG with transparency!!"); + int w = img.getWidth(); + int h = img.getHeight(); + // BufferedImage.TYPE_INT_RGB seems to be fastest (JDK1.4.1, + // OSX) + int destType = BufferedImage.TYPE_INT_RGB; + BufferedImage img2 = new BufferedImage(w, h, destType); + img2.createGraphics().drawImage(img, null, 0, 0); + img = img2; + } + writer = (ImageWriter) ImageIO.getImageWritersByFormatName( + "jpeg").next(); + if (writer == null) { + throw new FileOpException("Unable to get JPEG writer"); + } + ImageWriteParam param = writer.getDefaultWriteParam(); + if (quality > 1) { + // change JPEG compression quality + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + //logger.debug("JPEG qual before: " + // + Float.toString(param.getCompressionQuality())); + param.setCompressionQuality(0.9f); + //logger.debug("JPEG qual now: " + // + Float.toString(param.getCompressionQuality())); + } + writer.setOutput(imgout); + // render output + logger.debug("writing"); + writer.write(null, new IIOImage(img, null, null), param); + } else if (mt == "image/png") { + // render output + writer = (ImageWriter) ImageIO.getImageWritersByFormatName( + "png").next(); + if (writer == null) { + throw new FileOpException("Unable to get PNG writer"); + } + writer.setOutput(imgout); + logger.debug("writing"); + writer.write(img); + } else { + // unknown mime type + throw new FileOpException("Unknown mime type: " + mt); + } - if (croppedImg == null) { - util.dprintln(2, "ERROR(cropAndScale): error in crop"); - throw new ImageOpException("Unable to crop"); - } + } catch (IOException e) { + throw new FileOpException("Error writing image."); + } finally { + // clean up + if (writer != null) { + writer.dispose(); + } + } + } + + public void scale(double scale, double scaleY) throws ImageOpException { + logger.debug("scale"); + /* for downscaling in high quality the image is blurred first */ + if ((scale <= 0.5) && (quality > 1)) { + int bl = (int) Math.floor(1 / scale); + blur(bl); + } + /* then scaled */ + AffineTransformOp scaleOp = new AffineTransformOp(AffineTransform + .getScaleInstance(scale, scale), renderHint); + BufferedImage scaledImg = null; + // enforce destination image type (*Java2D BUG*) + int type = img.getType(); + // FIXME: which type would be best? + if ((quality > 0) && (type != 0)) { + logger.debug("creating destination image"); + Rectangle2D dstBounds = scaleOp.getBounds2D(img); + scaledImg = new BufferedImage((int) dstBounds.getWidth(), + (int) dstBounds.getHeight(), type); + } + logger.debug("scaling..."); + scaledImg = scaleOp.filter(img, scaledImg); + if (scaledImg == null) { + throw new ImageOpException("Unable to scale"); + } + // DEBUG + logger.debug("destination image type " + scaledImg.getType()); + logger.debug("SCALE: " + scale + " ->" + scaledImg.getWidth() + "x" + + scaledImg.getHeight()); + img = scaledImg; + scaledImg = null; + } - // setup scale - AffineTransformOp scaleOp = new AffineTransformOp( - AffineTransform.getScaleInstance(scale, scale), - scaleInt); - BufferedImage scaledImg = scaleOp.filter(croppedImg, null); - croppedImg = null; // free opCrop + public void blur(int radius) throws ImageOpException { + // DEBUG + logger.debug("blur: " + radius); + // minimum radius is 2 + int klen = Math.max(radius, 2); + // FIXME: use constant kernels for most common sizes + int ksize = klen * klen; + // kernel is constant 1/k + float f = 1f / ksize; + float[] kern = new float[ksize]; + for (int i = 0; i < ksize; i++) { + kern[i] = f; + } + Kernel blur = new Kernel(klen, klen, kern); + // blur with convolve operation + ConvolveOp blurOp = new ConvolveOp(blur, ConvolveOp.EDGE_NO_OP, + renderHint); + // blur needs explicit destination image type for color *Java2D BUG* + BufferedImage blurredImg = null; + if (img.getType() == BufferedImage.TYPE_3BYTE_BGR) { + blurredImg = new BufferedImage(img.getWidth(), img.getHeight(), img + .getType()); + } + blurredImg = blurOp.filter(img, blurredImg); + if (blurredImg == null) { + throw new ImageOpException("Unable to scale"); + } + img = blurredImg; + } + + public void crop(int x_off, int y_off, int width, int height) + throws ImageOpException { + // setup Crop + BufferedImage croppedImg = img.getSubimage(x_off, y_off, width, height); + // DEBUG + // util.dprintln(2, " time + // "+(System.currentTimeMillis()-startTime)+"ms"); + if (croppedImg == null) { + throw new ImageOpException("Unable to crop"); + } + logger.debug("CROP:" + croppedImg.getWidth() + "x" + + croppedImg.getHeight()); + img = croppedImg; + } + + public void enhance(float mult, float add) throws ImageOpException { + /* + * Only one constant should work regardless of the number of bands + * according to the JDK spec. Doesn't work on JDK 1.4 for OSX and Linux + * (at least). RescaleOp scaleOp = new RescaleOp( (float)mult, + * (float)add, null); scaleOp.filter(img, img); + */ + + /* The number of constants must match the number of bands in the image. */ + int ncol = img.getColorModel().getNumComponents(); + float[] dm = new float[ncol]; + float[] da = new float[ncol]; + for (int i = 0; i < ncol; i++) { + dm[i] = (float) mult; + da[i] = (float) add; + } + RescaleOp scaleOp = new RescaleOp(dm, da, null); + scaleOp.filter(img, img); + } + + public void enhanceRGB(float[] rgbm, float[] rgba) throws ImageOpException { + + /* + * The number of constants must match the number of bands in the image. + * We do only 3 (RGB) bands. + */ + + int ncol = img.getColorModel().getNumColorComponents(); + if ((ncol != 3) || (rgbm.length != 3) || (rgba.length != 3)) { + logger + .debug("ERROR(enhance): unknown number of color bands or coefficients (" + + ncol + ")"); + return; + } + RescaleOp scaleOp = new RescaleOp(rgbOrdered(rgbm), rgbOrdered(rgba), + null); + scaleOp.filter(img, img); + } - if (scaledImg == null) { - util.dprintln(2, "ERROR(cropAndScale): error in scale"); - throw new ImageOpException("Unable to scale"); - } - img = scaledImg; - } + /** + * Ensures that the array f is in the right order to map the images RGB + * components. (not shure what happens + */ + public float[] rgbOrdered(float[] fa) { + /* + * TODO: this is UGLY, UGLY!! + */ + float[] fb; + int t = img.getType(); + if (img.getColorModel().hasAlpha()) { + fb = new float[4]; + if ((t == BufferedImage.TYPE_INT_ARGB) + || (t == BufferedImage.TYPE_INT_ARGB_PRE)) { + // RGB Type + fb[0] = fa[0]; + fb[1] = fa[1]; + fb[2] = fa[2]; + fb[3] = 1f; + } else { + // this isn't tested :-( + fb[0] = 1f; + fb[1] = fa[0]; + fb[2] = fa[1]; + fb[3] = fa[2]; + } + } else { + fb = new float[3]; + if (t == BufferedImage.TYPE_3BYTE_BGR) { + // BGR Type (actually it looks like RBG...) + fb[0] = fa[0]; + fb[1] = fa[2]; + fb[2] = fa[1]; + } else { + fb[0] = fa[0]; + fb[1] = fa[1]; + fb[2] = fa[2]; + } + } + return fb; + } + public void rotate(double angle) throws ImageOpException { + // setup rotation + double rangle = Math.toRadians(angle); + // create offset to make shure the rotated image has no negative + // coordinates + double w = img.getWidth(); + double h = img.getHeight(); + AffineTransform trafo = new AffineTransform(); + // center of rotation + double x = (w / 2); + double y = (h / 2); + trafo.rotate(rangle, x, y); + // try rotation to see how far we're out of bounds + AffineTransformOp rotOp = new AffineTransformOp(trafo, renderHint); + Rectangle2D rotbounds = rotOp.getBounds2D(img); + double xoff = rotbounds.getX(); + double yoff = rotbounds.getY(); + // move image back in line + trafo + .preConcatenate(AffineTransform.getTranslateInstance(-xoff, + -yoff)); + // transform image + rotOp = new AffineTransformOp(trafo, renderHint); + BufferedImage rotImg = rotOp.filter(img, null); + // calculate new bounding box + // Rectangle2D bounds = rotOp.getBounds2D(img); + if (rotImg == null) { + throw new ImageOpException("Unable to rotate"); + } + img = rotImg; + // crop new image (with self-made rounding) + /* + * img = rotImg.getSubimage( (int) (bounds.getX()+0.5), (int) + * (bounds.getY()+0.5), (int) (bounds.getWidth()+0.5), (int) + * (bounds.getHeight()+0.5)); + */ + } + + public void mirror(double angle) throws ImageOpException { + // setup mirror + double mx = 1; + double my = 1; + double tx = 0; + double ty = 0; + if (Math.abs(angle - 0) < epsilon) { // 0 degree + mx = -1; + tx = getWidth(); + } else if (Math.abs(angle - 90) < epsilon) { // 90 degree + my = -1; + ty = getHeight(); + } else if (Math.abs(angle - 180) < epsilon) { // 180 degree + mx = -1; + tx = getWidth(); + } else if (Math.abs(angle - 270) < epsilon) { // 270 degree + my = -1; + ty = getHeight(); + } else if (Math.abs(angle - 360) < epsilon) { // 360 degree + mx = -1; + tx = getWidth(); + } + AffineTransformOp mirOp = new AffineTransformOp(new AffineTransform(mx, + 0, 0, my, tx, ty), renderHint); + BufferedImage mirImg = mirOp.filter(img, null); + if (mirImg == null) { + throw new ImageOpException("Unable to mirror"); + } + img = mirImg; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#finalize() + */ + protected void finalize() throws Throwable { + dispose(); + super.finalize(); + } + + public void dispose() { + // we must dispose the ImageReader because it keeps the filehandle + // open! + if (reader != null) { + reader.dispose(); + reader = null; + } + img = null; + } + + public Image getImage(){ + return (Image) img; + } + + }
--- a/servlet/src/digilib/image/JAIDocuImage.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/image/JAIDocuImage.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,168 +1,491 @@ /* JAIDocuImage -- Image class implementation using JAI (Java Advanced Imaging) - Digital Image Library servlet components + Digital Image Library servlet components - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ + */ package digilib.image; -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.ParameterBlock; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; -import java.awt.*; -import java.awt.image.*; -import java.awt.image.renderable.*; -import javax.media.jai.*; +import javax.media.jai.BorderExtender; +import javax.media.jai.Interpolation; +import javax.media.jai.JAI; +import javax.media.jai.KernelJAI; +import javax.media.jai.ParameterBlockJAI; +import javax.media.jai.RenderedOp; +import javax.media.jai.operator.TransposeDescriptor; +import javax.media.jai.operator.TransposeType; -import digilib.*; -import digilib.io.*; +import com.sun.media.jai.codec.ImageCodec; +import digilib.io.FileOpException; +import digilib.io.FileOps; +import digilib.io.ImageFile; +import digilib.io.ImageFileset; +/** A DocuImage implementation using Java Advanced Imaging Library. */ public class JAIDocuImage extends DocuImageImpl { - private RenderedImage img; + protected RenderedImage img; - public JAIDocuImage() { - } - - public JAIDocuImage(Utils u) { - util = u; - } + protected Interpolation interpol = null; - /** - * load image file - */ - public void loadImage(File f) throws FileOpException { - System.gc(); - img = JAI.create("fileload", f.getAbsolutePath()); - if (img == null) { - util.dprintln(3, "ERROR(loadImage): unable to load file"); - throw new FileOpException("Unable to load File!"); - } - } + /* + * static { // we could set our own tile cache size here TileCache tc = + * JAI.createTileCache(100*1024*1024); + * JAI.getDefaultInstance().setTileCache(tc); } + */ + + public boolean isSubimageSupported() { + return true; + } - /** - * write image of type mt to Stream - */ - public void writeImage(String mt, ServletResponse res) - throws FileOpException { - try { - // setup output - ParameterBlock pb3 = new ParameterBlock(); - pb3.addSource(img); - pb3.add(res.getOutputStream()); - if (mt == "image/jpeg") { - pb3.add("JPEG"); - } else if (mt == "image/png") { - pb3.add("PNG"); - } else { - // unknown mime type - util.dprintln(2, "ERROR(writeImage): Unknown mime type "+mt); - throw new FileOpException("Unknown mime type: "+mt); - } - res.setContentType(mt); - // render output - JAI.create("encode", pb3); - - } catch (IOException e) { - throw new FileOpException("Error writing image."); - } - } + /* + * Real setQuality implementation. Creates the correct Interpolation. + */ + public void setQuality(int qual) { + quality = qual; + // setup interpolation quality + if (qual > 1) { + logger.debug("quality q2"); + interpol = Interpolation.getInstance(Interpolation.INTERP_BICUBIC); + } else if (qual == 1) { + logger.debug("quality q1"); + interpol = Interpolation.getInstance(Interpolation.INTERP_BILINEAR); + } else { + logger.debug("quality q0"); + interpol = Interpolation.getInstance(Interpolation.INTERP_NEAREST); + } + } - public int getWidth() { - if (img != null) { - return img.getWidth(); - } - return 0; - } - - public int getHeight() { - if (img != null) { - return img.getHeight(); - } - return 0; - } - - - /** - * crop and scale image - * take rectangle width,height at position x_off,y_off - * and scale by scale - */ - public void cropAndScale(int x_off, int y_off, int width, int height, - float scale, int qual) throws ImageOpException { - - Interpolation scaleInt = null; - // setup interpolation quality - if (qual > 1) { - util.dprintln(4, "quality q2"); - scaleInt = Interpolation.getInstance(Interpolation.INTERP_BICUBIC); - } else if (qual == 1) { - util.dprintln(4, "quality q1"); - scaleInt = Interpolation.getInstance(Interpolation.INTERP_BILINEAR); - } else { - util.dprintln(4, "quality q0"); - scaleInt = Interpolation.getInstance(Interpolation.INTERP_NEAREST); + /* returns a list of supported image formats */ + @SuppressWarnings("unchecked") // ImageCodec.getCodecs() returns a naked Enumeration + public Iterator<String> getSupportedFormats() { + Enumeration<ImageCodec> codecs = ImageCodec.getCodecs(); + List<String> formats = new ArrayList<String>(); + for (ImageCodec codec = codecs.nextElement(); codecs.hasMoreElements(); codec = codecs + .nextElement()) { + logger.debug("known format:"+codec.getFormatName()); + formats.add(codec.getFormatName()); + } + logger.debug("tilecachesize:" + + JAI.getDefaultInstance().getTileCache().getMemoryCapacity()); + return formats.iterator(); } - // setup Crop - ParameterBlock pb1 = new ParameterBlock(); - pb1.addSource(img); - pb1.add((float)x_off); - pb1.add((float)y_off); - pb1.add((float)width); - pb1.add((float)height); - RenderedImage croppedImg = JAI.create("crop", pb1); - img = null; // free img + /* Check image size and type and store in ImageFile f */ + public boolean identify(ImageFile imgf) throws IOException { + // try parent method first + if (super.identify(imgf)) { + return true; + } + // fileset to store the information + ImageFileset imgfs = imgf.getParent(); + File f = imgf.getFile(); + if (f == null) { + throw new IOException("File not found!"); + } + /* + * try JAI + */ + logger.debug("identifying (JAI) " + f); + try { + RenderedOp img = JAI.create("fileload", f.getAbsolutePath()); + ImageSize d = new ImageSize(img.getWidth(), img.getHeight()); + imgf.setSize(d); + String t = FileOps.mimeForFile(f); + imgf.setMimetype(t); + // logger.debug(" format:"+t); + if (imgfs != null) { + imgfs.setAspect(d); + } + logger.debug("image size: " + imgf.getSize()); + return true; + } catch (Exception e) { + throw new FileOpException("ERROR: unknown image file format!"); + } + } + + /* Load an image file into the Object. */ + public void loadImage(ImageFile f) throws FileOpException { + img = JAI.create("fileload", f.getFile().getAbsolutePath()); + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } + + /* Load an image file into the Object. */ + public void loadSubimage(ImageFile f, Rectangle region, int subsample) + throws FileOpException { + logger.debug("loadSubimage"); + img = JAI.create("fileload", f.getFile().getAbsolutePath()); + if ((region.width < img.getWidth()) + || (region.height < img.getHeight())) { + // setup Crop + ParameterBlock cp = new ParameterBlock(); + cp.addSource(img); + cp.add((float) region.x); + cp.add((float) region.y); + cp.add((float) region.width); + cp.add((float) region.height); + logger.debug("loadSubimage: crop"); + img = JAI.create("crop", cp); + } + if (subsample > 1) { + float sc = 1f / subsample; + ParameterBlockJAI sp = new ParameterBlockJAI("scale"); + sp.addSource(img); + sp.setParameter("xScale", sc); + sp.setParameter("yScale", sc); + sp.setParameter("interpolation", Interpolation + .getInstance(Interpolation.INTERP_NEAREST)); + // scale + logger.debug("loadSubimage: scale"); + img = JAI.create("scale", sp); + } + } - util.dprintln(3, "CROP:"+croppedImg.getWidth()+"x"+croppedImg.getHeight()); //DEBUG + /* Write the current image to an OutputStream. */ + public void writeImage(String mt, OutputStream ostream) + throws FileOpException { + try { + // setup output + ParameterBlock pb3 = new ParameterBlock(); + pb3.addSource(img); + pb3.add(ostream); + if (mt == "image/jpeg") { + pb3.add("JPEG"); + } else if (mt == "image/png") { + pb3.add("PNG"); + } else { + // unknown mime type + throw new FileOpException("Unknown mime type: " + mt); + } + // render output + JAI.create("encode", pb3); + + } catch (IOException e) { + throw new FileOpException("Error writing image."); + } + } + + /** + * The width of the curent image in pixel. + * + * @return Image width in pixels. + */ + public int getWidth() { + if (img != null) { + return img.getWidth(); + } + return 0; + } + + /** + * The height of the curent image in pixel. + * + * @return Image height in pixels. + */ + public int getHeight() { + if (img != null) { + return img.getHeight(); + } + return 0; + } - if (croppedImg == null) { - util.dprintln(2, "ERROR(cropAndScale): error in crop"); - throw new ImageOpException("Unable to crop"); - } + /* scales the current image */ + public void scale(double scale, double scaleY) throws ImageOpException { + logger.debug("scale"); + if ((scale < 1) && (img.getColorModel().getPixelSize() == 1) + && (quality > 0)) { + /* + * "SubsampleBinaryToGray" for downscaling BW + */ + scaleBinary((float) scale); + } else if ((scale <= 0.5) && (quality > 1)) { + /* + * blur and "Scale" for downscaling color images + */ + if ((scale <= 0.5) && (quality > 1)) { + int bl = (int) Math.floor(1 / scale); + // don't blur more than 3 + blur(Math.min(bl, 3)); + } + scaleAll((float) scale); + } else { + /* + * "Scale" for the rest + */ + scaleAll((float) scale); + } + + // DEBUG + logger.debug("SCALE: " + scale + " ->" + img.getWidth() + "x" + + img.getHeight()); + + } + + public void scaleAll(float scale) throws ImageOpException { + RenderedImage scaledImg; + // DEBUG + logger.debug("scaleAll: " + scale); + ParameterBlockJAI param = new ParameterBlockJAI("Scale"); + param.addSource(img); + param.setParameter("xScale", scale); + param.setParameter("yScale", scale); + param.setParameter("interpolation", interpol); + // hint with border extender + RenderingHints hint = new RenderingHints(JAI.KEY_BORDER_EXTENDER, + BorderExtender.createInstance(BorderExtender.BORDER_COPY)); + // scale + scaledImg = JAI.create("Scale", param, hint); + + if (scaledImg == null) { + throw new ImageOpException("Unable to scale"); + } + img = scaledImg; + } - // setup scale - ParameterBlock pb2 = new ParameterBlock(); - pb2.addSource(croppedImg); - pb2.add(scale); - pb2.add(scale); - pb2.add(0f); - pb2.add(0f); - pb2.add(scaleInt); - // the following is nice but way too slow... - //if (opCrop.getColorModel().getPixelSize() < 8) { - // change color model if necessary - // util.dprintln("converting color model..."); - // BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY); - // ImageLayout lay = new ImageLayout(bi); - // rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, lay); - //} - RenderedImage scaledImg = JAI.create("scale", pb2); - croppedImg = null; // free opCrop + public void blur(int radius) throws ImageOpException { + RenderedImage blurredImg; + int klen = Math.max(radius, 2); + logger.debug("blur: " + klen); + int ksize = klen * klen; + float f = 1f / ksize; + float[] kern = new float[ksize]; + for (int i = 0; i < ksize; i++) { + kern[i] = f; + } + KernelJAI blur = new KernelJAI(klen, klen, kern); + ParameterBlockJAI param = new ParameterBlockJAI("Convolve"); + param.addSource(img); + param.setParameter("kernel", blur); + // hint with border extender + RenderingHints hint = new RenderingHints(JAI.KEY_BORDER_EXTENDER, + BorderExtender.createInstance(BorderExtender.BORDER_COPY)); + blurredImg = JAI.create("Convolve", param, hint); + if (blurredImg == null) { + throw new ImageOpException("Unable to scale"); + } + img = blurredImg; + } + + public void scaleBinary(float scale) throws ImageOpException { + RenderedImage scaledImg; + // DEBUG + logger.debug("scaleBinary: " + scale); + ParameterBlockJAI param = new ParameterBlockJAI("SubsampleBinaryToGray"); + param.addSource(img); + param.setParameter("xScale", scale); + param.setParameter("yScale", scale); + // hint with border extender + RenderingHints hint = new RenderingHints(JAI.KEY_BORDER_EXTENDER, + BorderExtender.createInstance(BorderExtender.BORDER_COPY)); + // scale + scaledImg = JAI.create("SubsampleBinaryToGray", param, hint); + if (scaledImg == null) { + throw new ImageOpException("Unable to scale"); + } + img = scaledImg; + } + + /* crops the current image */ + public void crop(int x_off, int y_off, int width, int height) + throws ImageOpException { + // setup Crop + ParameterBlock param = new ParameterBlock(); + param.addSource(img); + param.add((float) x_off); + param.add((float) y_off); + param.add((float) width); + param.add((float) height); + RenderedImage croppedImg = JAI.create("crop", param); + + logger.debug("CROP: " + x_off + "," + y_off + ", " + width + "," + + height + " ->" + croppedImg.getWidth() + "x" + + croppedImg.getHeight()); + // DEBUG + + if (croppedImg == null) { + throw new ImageOpException("Unable to crop"); + } + img = croppedImg; + } + + /* rotates the current image */ + public void rotate(double angle) throws ImageOpException { + RenderedImage rotImg; + // convert degrees to radians + double rangle = Math.toRadians(angle); + double x = img.getWidth() / 2; + double y = img.getHeight() / 2; + + // optimize rotation by right angles + TransposeType rotOp = null; + if (Math.abs(angle - 0) < epsilon) { + // 0 degree + return; + } else if (Math.abs(angle - 90) < epsilon) { + // 90 degree + rotOp = TransposeDescriptor.ROTATE_90; + } else if (Math.abs(angle - 180) < epsilon) { + // 180 degree + rotOp = TransposeDescriptor.ROTATE_180; + } else if (Math.abs(angle - 270) < epsilon) { + // 270 degree + rotOp = TransposeDescriptor.ROTATE_270; + } else if (Math.abs(angle - 360) < epsilon) { + // 360 degree + return; + } + if (rotOp != null) { + // use Transpose operation + ParameterBlock pb = new ParameterBlock(); + pb.addSource(img); + pb.add(rotOp); + rotImg = JAI.create("transpose", pb); + } else { + // setup "normal" rotation + ParameterBlock param = new ParameterBlock(); + param.addSource(img); + param.add((float) x); + param.add((float) y); + param.add((float) rangle); + param.add(interpol); - if (scaledImg == null) { - util.dprintln(2, "ERROR(cropAndScale): error in scale"); - throw new ImageOpException("Unable to scale"); - } + rotImg = JAI.create("rotate", param); + } + + logger.debug("ROTATE: " + x + "," + y + ", " + angle + " (" + rangle + + ")" + " ->" + rotImg.getWidth() + "x" + rotImg.getHeight()); + // DEBUG + + if (rotImg == null) { + throw new ImageOpException("Unable to rotate"); + } + img = rotImg; + } + + /* + * mirrors the current image works only horizontal and vertical + */ + public void mirror(double angle) throws ImageOpException { + RenderedImage mirImg; + // only mirroring by right angles + TransposeType rotOp = null; + if (Math.abs(angle) < epsilon) { + // 0 degree + rotOp = TransposeDescriptor.FLIP_HORIZONTAL; + } else if (Math.abs(angle - 90) < epsilon) { + // 90 degree + rotOp = TransposeDescriptor.FLIP_VERTICAL; + } else if (Math.abs(angle - 180) < epsilon) { + // 180 degree + rotOp = TransposeDescriptor.FLIP_HORIZONTAL; + } else if (Math.abs(angle - 270) < epsilon) { + // 270 degree + rotOp = TransposeDescriptor.FLIP_VERTICAL; + } else if (Math.abs(angle - 360) < epsilon) { + // 360 degree + rotOp = TransposeDescriptor.FLIP_HORIZONTAL; + } + // use Transpose operation + ParameterBlock param = new ParameterBlock(); + param.addSource(img); + param.add(rotOp); + mirImg = JAI.create("transpose", param); + + if (mirImg == null) { + throw new ImageOpException("Unable to flip"); + } + img = mirImg; + } - img = scaledImg; - } + /* contrast and brightness enhancement */ + public void enhance(float mult, float add) throws ImageOpException { + RenderedImage enhImg; + double[] ma = { mult }; + double[] aa = { add }; + // use Rescale operation + ParameterBlock param = new ParameterBlock(); + param.addSource(img); + param.add(ma); + param.add(aa); + enhImg = JAI.create("rescale", param); + + logger.debug("ENHANCE: *" + mult + ", +" + add + " ->" + + enhImg.getWidth() + "x" + enhImg.getHeight()); + // DEBUG + + if (enhImg == null) { + throw new ImageOpException("Unable to enhance"); + } + img = enhImg; + } + + /* + * (non-Javadoc) + * + * @see digilib.image.DocuImage#enhanceRGB(float[], float[]) + */ + public void enhanceRGB(float[] rgbm, float[] rgba) throws ImageOpException { + RenderedImage enhImg; + int nb = rgbm.length; + double[] ma = new double[nb]; + double[] aa = new double[nb]; + for (int i = 0; i < nb; i++) { + ma[i] = rgbm[i]; + aa[i] = rgba[i]; + } + // use Rescale operation + ParameterBlock param = new ParameterBlock(); + param.addSource(img); + param.add(ma); + param.add(aa); + enhImg = JAI.create("rescale", param); + + logger.debug("ENHANCE_RGB: *" + rgbm + ", +" + rgba + " ->" + + enhImg.getWidth() + "x" + enhImg.getHeight()); + // DEBUG + + if (enhImg == null) { + throw new ImageOpException("Unable to enhanceRGB"); + } + img = enhImg; + } + + /* + * (non-Javadoc) + * + * @see digilib.image.DocuImage#dispose() + */ + public void dispose() { + img = null; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/image/JAIImageLoaderDocuImage.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,184 @@ +/* JAIImageLoaderDocuImage -- Image class implementation using JAI's ImageLoader Plugin + + Digital Image Library servlet components + + Copyright (C) 2002, 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +package digilib.image; + +import java.awt.Rectangle; +import java.awt.image.renderable.ParameterBlock; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.Iterator; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; +import javax.media.jai.JAI; + +import digilib.io.FileOpException; +import digilib.io.ImageFile; + +/** DocuImage implementation using the Java Advanced Imaging API and the ImageLoader + * API of Java 1.4. + */ +public class JAIImageLoaderDocuImage extends JAIDocuImage { + + /** ImageIO image reader */ + protected ImageReader reader; + /** current image file */ + protected File imgFile; + + /* loadSubimage is supported. */ + public boolean isSubimageSupported() { + return true; + } + + public int getHeight() { + int h = 0; + try { + if (img == null) { + h = reader.getHeight(0); + } else { + h = img.getHeight(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return h; + } + + public int getWidth() { + int w = 0; + try { + if (img == null) { + w = reader.getWidth(0); + } else { + w = img.getWidth(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return w; + } + + /* Load an image file into the Object. */ + public void loadImage(ImageFile f) throws FileOpException { + logger.debug("loadImage: "+f.getFile()); + //System.gc(); + img = JAI.create("ImageRead", f.getFile().getAbsolutePath()); + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } + + /* Get an ImageReader for the image file. */ + public ImageReader getReader(ImageFile f) throws IOException { + logger.debug("preloadImage: "+f.getFile()); + //System.gc(); + RandomAccessFile rf = new RandomAccessFile(f.getFile(), "r"); + ImageInputStream istream = new FileImageInputStream(rf); + //Iterator readers = ImageIO.getImageReaders(istream); + Iterator<ImageReader> readers = ImageIO.getImageReadersByMIMEType(f.getMimetype()); + if (! readers.hasNext()) { + throw new FileOpException("Unable to load File!"); + } + reader = readers.next(); + logger.debug("JAIImageIO: this reader: " + reader.getClass()); + while (readers.hasNext()) { + logger.debug(" next reader: " + readers.next().getClass()); + } + reader.setInput(istream); + return reader; + } + + /* Load an image file into the Object. */ + public void loadSubimage(ImageFile f, Rectangle region, int prescale) + throws FileOpException { + logger.debug("loadSubimage: "+f.getFile()); + //System.gc(); + try { + if ((reader == null) || (imgFile != f.getFile())) { + getReader(f); + } + ImageReadParam readParam = reader.getDefaultReadParam(); + readParam.setSourceRegion(region); + readParam.setSourceSubsampling(prescale, prescale, 0, 0); + img = reader.read(0, readParam); + /* JAI imageread seems to ignore the readParam :-( + ImageInputStream istream = (ImageInputStream) reader.getInput(); + ParameterBlockJAI pb = new ParameterBlockJAI("imageread"); + pb.setParameter("Input", istream); + pb.setParameter("ReadParam", readParam); + pb.setParameter("Reader", reader); + img = JAI.create("imageread", pb); + */ + } catch (IOException e) { + throw new FileOpException("Unable to load File!"); + } + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + imgFile = f.getFile(); + } + + + /* Write the current image to an OutputStream. */ + public void writeImage(String mt, OutputStream ostream) + throws FileOpException { + logger.debug("writeImage"); + try { + // setup output + ParameterBlock pb3 = new ParameterBlock(); + pb3.addSource(img); + pb3.add(ostream); + if (mt == "image/jpeg") { + pb3.add("JPEG"); + } else if (mt == "image/png") { + pb3.add("PNG"); + } else { + // unknown mime type + throw new FileOpException("Unknown mime type: " + mt); + } + // render output + JAI.create("ImageWrite", pb3); + } catch (IOException e) { + throw new FileOpException("Error writing image."); + } + } + + /* (non-Javadoc) + * @see java.lang.Object#finalize() + */ + protected void finalize() throws Throwable { + dispose(); + super.finalize(); + } + + public void dispose() { + // we must dispose the ImageReader because it keeps the filehandle open! + reader.dispose(); + reader = null; + img = null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/AliasingDocuDirCache.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,96 @@ +/* + * AliasingDocuDirCache -- DocuDirCache using alias entries from config file + * + * Digital Image Library servlet components + * + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 04.11.2003 + */ + +package digilib.io; + +import java.io.File; +import java.util.Map; +import java.util.Map.Entry; + +import digilib.servlet.DigilibConfiguration; + +/** + * @author casties + * + */ +public class AliasingDocuDirCache extends DocuDirCache { + + /** + * @param baseDirs + * @param fileClasses + * @param confFileName + * @throws FileOpException + */ + public AliasingDocuDirCache(String[] baseDirs, int[] fileClasses, + File confFile, DigilibConfiguration dlConfig) + throws FileOpException { + // create standard DocuDirCache + super(baseDirs, fileClasses, dlConfig); + Map<String,String> pathMap = null; + // read alias config file + try { + // load into pathMap + XMLListLoader mapLoader = new XMLListLoader("digilib-aliases", + "mapping", "link", "dir"); + pathMap = mapLoader.loadURL(confFile.toURL().toString()); + } catch (Exception e) { + throw new FileOpException("ERROR loading mapping file: " + e); + } + if (pathMap == null) { + throw new FileOpException("ERROR: unable to load mapping file!"); + } + + /* + * load map entries into cache + */ + + for (Entry<String, String> linkdir: pathMap.entrySet()) { + if (linkdir.getValue() == null) { + logger.error("Key mismatch in mapping file!"); + break; + } + DocuDirectory destDir = new DocuDirectory(linkdir.getValue(), this); + if (destDir.isValid()) { + logger.debug("Aliasing dir: " + linkdir.getKey()); + // add the alias name + putName(FileOps.normalName(linkdir.getKey()), destDir); + // add the real dir + putDir(destDir); + } + } + } + + /** + * Adds a DocuDirectory under another name to the cache. + * + * @param name + * @param newdir + */ + public void putName(String name, DocuDirectory newdir) { + if (map.containsKey(name)) { + logger.warn("Duplicate key in AliasingDocuDirCache.put -- ignored!"); + } else { + map.put(name, newdir); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/DocuDirCache.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,361 @@ +/* + * DocuDirCache.java + * + * Digital Image Library servlet components + * + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 03.03.2003 + */ + +package digilib.io; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import digilib.servlet.DigilibConfiguration; + +/** + * @author casties + */ +public class DocuDirCache { + + /** general logger for this class */ + Logger logger = Logger.getLogger(this.getClass()); + + /** HashMap of directories */ + Map<String, DocuDirectory> map = null; + + /** names of base directories */ + String[] baseDirNames = null; + + /** array of allowed file classes (image/text) */ + private int[] fileClasses = null; + + /** number of files in the whole cache (approximate) */ + long numFiles = 0; + + /** number of cache hits */ + long hits = 0; + + /** number of cache misses */ + long misses = 0; + + /** use safe (but slow) indexing */ + boolean safeDirIndex = false; + + /** the root directory element */ + public static Directory ROOT = null; + + /** + * Constructor with array of base directory names and file classes. + * + * @param bd + * base directory names + */ + public DocuDirCache(String[] bd, int[] fileClasses, + DigilibConfiguration dlConfig) { + baseDirNames = bd; + map = new HashMap<String, DocuDirectory>(); + this.fileClasses = fileClasses; + safeDirIndex = dlConfig.getAsBoolean("safe-dir-index"); + } + + /** + * Constructor with array of base directory names. + * + * @param bd + * base directory names + */ + public DocuDirCache(String[] bd) { + baseDirNames = bd; + map = new HashMap<String, DocuDirectory>(); + // default file class is CLASS_IMAGE + fileClasses = new int[1]; + fileClasses[0] = FileOps.CLASS_IMAGE; + } + + /** + * The number of directories in the cache. + * + * @return + */ + public int size() { + return (map != null) ? map.size() : 0; + } + + /** + * Add a DocuDirectory to the cache. + * + * @param newdir + */ + public void put(DocuDirectory newdir) { + String s = newdir.getDirName(); + if (map.containsKey(s)) { + logger.warn("Duplicate key in DocuDirCache.put -- ignoring!"); + } else { + map.put(s, newdir); + numFiles += newdir.size(); + } + } + + /** + * Add a directory to the cache and check its parents. + * + * @param newDir + */ + public synchronized void putDir(DocuDirectory newDir) { + put(newDir); + String parent = FileOps.parent(newDir.getDirName()); + if (parent != "") { + // check the parent in the cache + DocuDirectory pd = (DocuDirectory) map.get(parent); + if (pd == null) { + // the parent is unknown + pd = new DocuDirectory(parent, this); + putDir(pd); + } + newDir.setParent(pd); + } + } + + /** + * Get a list with all children of a directory. + * + * Returns a List of DocuDirectory's. Returns an empty List if the directory + * has no children. If recurse is false then only direct children are + * returned. + * + * @param dirname + * @param recurse + * find all children and their children. + * @return + */ + public List<DocuDirectory> getChildren(String dirname, boolean recurse) { + List<DocuDirectory> l = new LinkedList<DocuDirectory>(); + for (DocuDirectory dd: map.values()) { + if (recurse) { + if (dd.getDirName().startsWith(dirname)) { + l.add(dd); + } + } else { + if (FileOps.parent(dd.getDirName()).equals(dirname)) { + l.add(dd); + } + } + } + return l; + } + + /** + * Returns the DocuDirent with the pathname <code>fn</code> and the index + * <code>in</code> and the class <code>fc</code>. + * + * If <code>fn</code> is a file then the corresponding DocuDirent is + * returned and the index is ignored. + * + * @param fn + * digilib pathname + * @param in + * file index + * @param fc + * file class + * @return + */ + public DocuDirent getFile(String fn, int in, int fc) { + DocuDirectory dd; + // file number is 1-based, vector index is 0-based + int n = in - 1; + // first, assume fn is a directory and look in the cache + dd = (DocuDirectory) map.get(fn); + // logger.debug("fn: " + fn); + // logger.debug("dd: " + dd); + if (dd == null) { + // cache miss + misses++; + /* + * see if fn is a directory + */ + File f = new File(baseDirNames[0], fn); + if (f.isDirectory()) { + // logger.debug(fn + " is a dir"); + dd = new DocuDirectory(fn, this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } + } else { + /* + * maybe it's a file + */ + // get the parent directory string (like we store it in the + // cache) + String d = FileOps.parent(fn); + // try it in the cache + // logger.debug(fn + " is a file in dir " + d); + dd = (DocuDirectory) map.get(d); + if (dd == null) { + // try to read from disk + dd = new DocuDirectory(d, this); + if (dd.isValid()) { + // add to the cache + // logger.debug(dd + " is valid"); + putDir(dd); + } else { + // invalid path + return null; + } + } else { + // it was not a real cache miss + misses--; + } + // get the file's index + n = dd.indexOf(f.getName(), fc); + // logger.debug(f.getName() + ", index is " + n + ", fc = " + fc); + } + } else { + // cache hit + hits++; + } + dd.refresh(); + // logger.debug(dd + " refreshed"); + if (dd.isValid()) { + try { + // logger.debug(dd + " is valid"); + return dd.get(n, fc); + } catch (IndexOutOfBoundsException e) { + // logger.debug(fn + " not found in directory"); + } + } + return null; + } + + /** + * Returns the DocuDirectory indicated by the pathname <code>fn</code>. + * + * If <code>fn</code> is a file then its parent directory is returned. + * + * @param fn + * digilib pathname + * @return + */ + public DocuDirectory getDirectory(String fn) { + DocuDirectory dd; + // first, assume fn is a directory and look in the cache + dd = (DocuDirectory) map.get(fn); + if (dd == null) { + // cache miss + misses++; + // see if it's a directory + File f = new File(baseDirNames[0], fn); + if (f.isDirectory()) { + dd = new DocuDirectory(fn, this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } + } else { + // maybe it's a file + if (f.canRead()) { + // try the parent directory in the cache + dd = (DocuDirectory) map.get(f.getParent()); + if (dd == null) { + // try to read from disk + dd = new DocuDirectory(f.getParent(), this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } else { + // invalid path + return null; + } + } else { + // not a real cache miss then + misses--; + } + } else { + // it's not even a file :-( + return null; + } + } + } else { + // cache hit + hits++; + } + dd.refresh(); + if (dd.isValid()) { + return dd; + } + return null; + } + + /** + * @return String[] + */ + public String[] getBaseDirNames() { + return baseDirNames; + } + + /** + * @return long + */ + public long getNumFiles() { + return numFiles; + } + + /** + * Sets the baseDirNames. + * + * @param baseDirNames + * The baseDirNames to set + */ + public void setBaseDirNames(String[] baseDirNames) { + this.baseDirNames = baseDirNames; + } + + /** + * @return long + */ + public long getHits() { + return hits; + } + + /** + * @return long + */ + public long getMisses() { + return misses; + } + + /** + * @return + */ + public int[] getFileClasses() { + return fileClasses; + } + + /** + * @param fileClasses + */ + public void setFileClasses(int[] fileClasses) { + this.fileClasses = fileClasses; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/DocuDirectory.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,576 @@ +/* DocuDirectory -- Directory of DocuFilesets. + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * Created on 25.02.2003 + */ + +package digilib.io; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.xml.sax.SAXException; + +/** + * @author casties + */ +public class DocuDirectory extends Directory { + + /** list of files (DocuDirent) */ + private List<List<DocuDirent>> list = null; + + /** directory object is valid (exists on disk) */ + private boolean isValid = false; + + /** reference of the parent DocuDirCache */ + private DocuDirCache cache = null; + + /** directory name (digilib canonical form) */ + private String dirName = null; + + /** directory metadata */ + private MetadataMap dirMeta = null; + + /** state of metadata is valid */ + private boolean metaChecked = false; + + /** unresolved file metadata */ + private Map<String, MetadataMap> unresolvedFileMeta = null; + + /** time of last access of this object (not the filesystem) */ + private long objectATime = 0; + + /** time directory was last modified on the file system */ + private long dirMTime = 0; + + /** + * Constructor with digilib directory path and a parent DocuDirCache. + * + * Directory names at the given path are appended to the base directories + * from the cache. The directory is checked on disk and isValid is set. + * + * @see readDir + * + * @param path + * digilib directory path name + * @param cache + * parent DocuDirCache + */ + public DocuDirectory(String path, DocuDirCache cache) { + this.dirName = path; + this.cache = cache; + initDir(); + checkDir(); + } + + /** + * Sets and checks the dir object. + * + */ + protected void initDir() { + String baseDirName = cache.getBaseDirNames()[0]; + // clear directory first + //list = new ArrayList[FileOps.NUM_CLASSES]; + + list = new ArrayList<List<DocuDirent>>(FileOps.NUM_CLASSES); + // create empty list + for (int i=0; i < FileOps.NUM_CLASSES; ++i) { + list.add(null); + } + isValid = false; + dirMTime = 0; + // the first directory has to exist + dir = new File(baseDirName, dirName); + } + + /** + * number of DocuFiles in this directory. + * + */ + public int size() { + return ((list != null) && (list.get(0) != null)) ? list.get(0).size() : 0; + } + + /** + * number of files of this class in this directory. + * + * @param fc + * fileClass + */ + public int size(int fc) { + return ((list != null) && (list.get(fc) != null)) ? list.get(fc).size() : 0; + } + + /** + * Returns the ImageFile at the index. + * + * @param index + * @return + */ + public ImageFileset get(int index) { + if ((list == null) || (list.get(0) == null) || (index >= list.get(0).size())) { + return null; + } + return (ImageFileset) list.get(0).get(index); + } + + /** + * Returns the file of the class at the index. + * + * @param index + * @param fc + * fileClass + * @return + */ + public DocuDirent get(int index, int fc) { + if ((list == null) || (list.get(fc) == null) || (index >= list.get(fc).size())) { + return null; + } + return (DocuDirent) list.get(fc).get(index); + } + + /** + * Checks if the directory exists on the filesystem. + * + * Sets isValid. + * + * @return + */ + public boolean checkDir() { + if (dir == null) { + initDir(); + } + isValid = dir.isDirectory(); + return isValid; + } + + /** + * Read the filesystem directory and fill this object. + * + * Clears the List and (re)reads all files. + * + * @return boolean the directory exists + */ + public synchronized boolean readDir() { + // check directory first + checkDir(); + if (!isValid) { + return false; + } + // first file extension to try for scaled directories + String scalext = null; + // read all filenames + logger.debug("reading directory " + dir.getPath()); + /* + * using ReadableFileFilter is safer (we won't get directories with file + * extensions) but slower. + */ + File[] allFiles = null; + if (cache.safeDirIndex) { + allFiles = dir.listFiles(new FileOps.ReadableFileFilter()); + } else { + allFiles = dir.listFiles(); + } + //logger.debug(" done"); + if (allFiles == null) { + // not a directory + return false; + } + // list of base dirs from the parent cache + String[] baseDirNames = cache.getBaseDirNames(); + // number of base dirs + int nb = baseDirNames.length; + // array of base dirs + Directory[] dirs = new Directory[nb]; + // first entry is this directory + dirs[0] = this; + // fill array with the remaining directories + for (int j = 1; j < nb; j++) { + File d = new File(baseDirNames[j], dirName); + if (d.isDirectory()) { + dirs[j] = new Directory(d); + logger.debug(" reading scaled directory " + d.getPath()); + dirs[j].readDir(); + //logger.debug(" done"); + } + } + + // go through all file classes + for (int classIdx = 0; classIdx < FileOps.NUM_CLASSES; classIdx++) { + int fileClass = cache.getFileClasses()[classIdx]; + //logger.debug("filtering directory "+dir.getPath()+" for class + // "+fc); + File[] fileList = FileOps.listFiles(allFiles, FileOps + .filterForClass(fileClass)); + //logger.debug(" done"); + // number of files in the directory + int numFiles = fileList.length; + if (numFiles > 0) { + // create new list + list.set(fileClass, new ArrayList<DocuDirent>(numFiles)); + // sort the file names alphabetically and iterate the list + // Arrays.sort(fileList); // not needed <hertzhaft> + Map<Integer, Object> hints = FileOps.newHints(FileOps.HINT_BASEDIRS, dirs); + hints.put(FileOps.HINT_FILEEXT, scalext); + for (int i = 0; i < numFiles; i++) { + DocuDirent f = FileOps.fileForClass(fileClass, fileList[i], + hints); + // add the file to our list + // logger.debug(f.getName()); + + list.get(fileClass).add(f); + f.setParent(this); + } + // we sort the inner ArrayList (the list of files not the list of file types) + // for binarySearch to work (DocuDirent's natural sort order is by filename) + Collections.sort(list.get(fileClass)); + } + } + // clear the scaled directories + for (int j = 1; j < nb; j++) { + if (dirs[j] != null) { + dirs[j].clearFilenames(); + } + } + // update number of cached files if this was the first time + if (dirMTime == 0) { + cache.numFiles += size(); + } + dirMTime = dir.lastModified(); + // read metadata as well + readMeta(); + return isValid; + } + + /** + * Check to see if the directory has been modified and reread if necessary. + * + * @return boolean the directory is valid + */ + public boolean refresh() { + if (isValid) { + if (dir.lastModified() > dirMTime) { + // on-disk modification time is more recent + readDir(); + } + touch(); + } + return isValid; + } + + /** + * Read directory metadata. + * + */ + public void readMeta() { + // check for directory metadata... + File mf = new File(dir, "index.meta"); + if (mf.canRead()) { + XMLMetaLoader ml = new XMLMetaLoader(); + try { + // read directory meta file + Map<String, MetadataMap> fileMeta = ml.loadURL(mf.getAbsolutePath()); + if (fileMeta == null) { + return; + } + // meta for the directory itself is in the "" bin + dirMeta = fileMeta.remove(""); + // read meta for files in this directory + readFileMeta(fileMeta, null); + // is there meta for other files left? + if (fileMeta.size() > 0) { + unresolvedFileMeta = fileMeta; + } + } catch (SAXException e) { + logger.warn("error parsing index.meta", e); + } catch (IOException e) { + logger.warn("error reading index.meta", e); + } + } + readParentMeta(); + metaChecked = true; + } + + /** + * Read metadata from all known parent directories. + * + */ + public void readParentMeta() { + // check the parent directories for additional file meta + Directory dd = parent; + String path = dir.getName(); + while (dd != null) { + if (((DocuDirectory) dd).hasUnresolvedFileMeta()) { + readFileMeta(((DocuDirectory) dd).unresolvedFileMeta, path); + } + // prepend parent dir path + path = dd.dir.getName() + "/" + path; + // become next parent + dd = dd.parent; + } + } + + /** + * Read metadata for the files in this directory. + * + * Takes a Map with meta-information, adding the relative path before the + * lookup. + * + * @param fileMeta + * @param relPath + * @param fc + * fileClass + */ + protected void readFileMeta(Map<String,MetadataMap> fileMeta, String relPath) { + if (list == null) { + // there are no files + return; + } + String path = (relPath != null) ? (relPath + "/") : ""; + // go through all file classes + for (int nc = 0; nc < list.size(); nc++) { + int fc = cache.getFileClasses()[nc]; + if (list.get(fc) == null) { + continue; + } + // iterate through the list of files in this directory + for (DocuDirent f: list.get(fc)) { + // prepend path to the filename + String fn = path + f.getName(); + // look up meta for this file and remove from dir + MetadataMap meta = fileMeta.remove(fn); + if (meta != null) { + // store meta in file + f.setFileMeta(meta); + } + } + } + } + + protected void notifyChildMeta(MetadataMap childmeta) { + List<DocuDirectory> children = cache.getChildren(this.dirName, true); + if (children.size() > 0) { + /*for (DocuDirectory d: children) { + // TODO: finish this! + //((DocuDirectory) i.next()).readFileMeta() + }*/ + } + } + + /** + * Update access time. + * + * @return long time of last access. + */ + public long touch() { + long t = objectATime; + objectATime = System.currentTimeMillis(); + return t; + } + + /** + * Searches for the file with the name <code>fn</code>. + * + * Searches the directory for the file with the name <code>fn</code> and + * returns its index. Returns -1 if the file cannot be found. + * + * @param fn + * filename + * @param fc + * file class + * @return int index of file <code>fn</code> + */ + public int indexOf(String fn) { + int fc = FileOps.classForFilename(fn); + return indexOf(fn, fc); + } + + /** + * Searches for the file with the name <code>fn</code> and class fc. + * + * Searches the directory for the file with the name <code>fn</code> and + * returns its index. Returns -1 if the file cannot be found. + * + * @param fn + * filename + * @return int index of file <code>fn</code> + */ + public int indexOf(String fn, int fc) { + if (!isRead()) { + // read directory now + if (!readDir()) { + return -1; + } + } + List<DocuDirent> fileList = list.get(fc); + // empty directory? + if (fileList == null) { + return -1; + } + + // search for exact match (DocuDirent does compareTo<String>) + // OBS: fileList needs to be sorted first (see )! <hertzhaft> + int idx = Collections.binarySearch(fileList, fn); + if (idx >= 0) { + return idx; + } else { + logger.debug(fn + " not found by binarysearch"); + // try closest matches without extension + idx = -idx - 1; + if ((idx < fileList.size()) + && isBaseInList(fileList, idx, fn)) { + // idx matches + return idx; + } else if ((idx > 0) + && isBaseInList(fileList, idx-1, fn)) { + // idx-1 matches + return idx - 1; + } else if ((idx + 1 < fileList.size()) + && isBaseInList(fileList, idx+1, fn)) { + // idx+1 matches + return idx + 1; + } + + } + return -1; + } + + private boolean isBaseInList(List<DocuDirent> fl, int idx, String fn) { + String dfn = FileOps.basename((fl.get(idx)) + .getName()); + return (dfn.equals(fn)||dfn.equals(FileOps.basename(fn))); + } + + + /** + * Finds the DocuDirent with the name <code>fn</code>. + * + * Searches the directory for the DocuDirent with the name <code>fn</code> + * and returns it. Returns null if the file cannot be found. + * + * @param fn + * filename + * @return DocuDirent + */ + public DocuDirent find(String fn) { + int fc = FileOps.classForFilename(fn); + int i = indexOf(fn, fc); + if (i >= 0) { + return (DocuDirent) list.get(0).get(i); + } + return null; + } + + /** + * Finds the DocuDirent with the name <code>fn</code> and class + * <code>fc</code>. + * + * Searches the directory for the DocuDirent with the name <code>fn</code> + * and returns it. Returns null if the file cannot be found. + * + * @param fn + * filename + * @return DocuDirent + */ + public DocuDirent find(String fn, int fc) { + int i = indexOf(fn, fc); + if (i >= 0) { + return (DocuDirent) list.get(fc).get(i); + } + return null; + } + + /** + * Returns the digilib canonical name. + * + * @return + */ + public String getDirName() { + return dirName; + } + + /** + * The directory is valid (exists on disk). + * + * @return boolean + */ + public boolean isValid() { + return isValid; + } + + /** + * The directory has been read from disk. + * + * @return + */ + public boolean isRead() { + return (dirMTime != 0); + } + + /** + * @return long + */ + public long getAccessTime() { + return objectATime; + } + + /** + * @return Hashtable + */ + public MetadataMap getDirMeta() { + return dirMeta; + } + + /** + * Checks metadata + * + */ + public void checkMeta() { + if (metaChecked) { + return; + } else { + readMeta(); + } + } + + /** + * @return long + */ + public long getDirMTime() { + return dirMTime; + } + + /** + * Sets the dirMeta. + * + * @param dirMeta + * The dirMeta to set + */ + public void setDirMeta(MetadataMap dirMeta) { + this.dirMeta = dirMeta; + } + + public boolean hasUnresolvedFileMeta() { + return (this.unresolvedFileMeta != null); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/DocuDirent.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,167 @@ +/* + * DocuDirent.java -- Abstract directory entry in a DocuDirectory + * + * Digital Image Library servlet components + * + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 15.09.2003 by casties + * + */ +package digilib.io; + +import java.io.File; +import java.util.Map; + +import org.apache.log4j.Logger; + +/** + * Abstract directory entry in a DocuDirectory. + * + * @author casties + * + */ +public abstract class DocuDirent implements Comparable<Object> { + + /** the file class of this file */ + protected static int fileClass = FileOps.CLASS_NONE; + /** HashMap with metadata */ + protected MetadataMap fileMeta = null; + /** Is the Metadata valid */ + protected boolean metaChecked = false; + /** the parent directory */ + protected Directory parent = null; + + /** + * Checks metadata and does something with it. + * + */ + public abstract void checkMeta(); + + /** + * gets the (default) File + * + * @return + */ + public abstract File getFile(); + + /** + * Reads meta-data for this Fileset if there is any. + * + */ + public void readMeta() { + if ((fileMeta != null) || (getFile() == null)) { + // there is already metadata or there is no file + return; + } + // metadata is in the file {filename}.meta + String fn = getFile().getAbsolutePath(); + File mf = new File(fn + ".meta"); + if (mf.canRead()) { + XMLMetaLoader ml = new XMLMetaLoader(); + try { + // read meta file + Map<String, MetadataMap> meta = ml.loadURL(mf.getAbsolutePath()); + if (meta == null) { + return; + } + fileMeta = meta.get(getName()); + } catch (Exception e) { + Logger.getLogger(this.getClass()).warn("error reading file .meta", e); + } + } + } + + /** + * The name of the file. + * + * If this is a Fileset, the method returns the name of the default file + * (for image filesets the highest resolution file). + * + * @return + */ + public String getName() { + File f = getFile(); + return (f != null) ? f.getName() : null; + } + + /** + * Returns the parent Directory. + * + * @return DocuDirectory + */ + public Directory getParent() { + return parent; + } + + /** + * Sets the parent Directory. + * + * @param parent + * The parent to set + */ + public void setParent(Directory parent) { + this.parent = parent; + } + + /** + * Returns the meta-data for this file(set). + * + * @return HashMap + */ + public MetadataMap getFileMeta() { + return fileMeta; + } + + /** + * Sets the meta-data for this file(set) . + * + * @param fileMeta + * The fileMeta to set + */ + public void setFileMeta(MetadataMap fileMeta) { + this.fileMeta = fileMeta; + } + + /** + * @return + */ + public boolean isMetaChecked() { + return metaChecked; + } + + /** + * @return + */ + public static int getFileClass() { + return fileClass; + } + + /** Comparator using the file name. + * Compares to a String (for binarySearch) + * or to another DocuDirent (for sort) + * + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object arg0) { + if (arg0 instanceof DocuDirent) { + return getName().compareTo(((DocuDirent) arg0).getName()); + } else { + return getName().compareTo((String) arg0); + } + } + + +}
--- a/servlet/src/digilib/io/FileOps.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/io/FileOps.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,188 +1,403 @@ -/* FileOps -- Utility class for file operations - - Digital Image Library servlet components - - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ +/* + * FileOps -- Utility class for file operations + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package digilib.io; -import java.io.*; -import java.util.*; - -import digilib.*; - +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; public class FileOps { - private Utils util = null; - public static String[] fileTypes = { - "jpg", "image/jpeg", - "jpeg", "image/jpeg", - "png", "image/png", - "gif", "image/gif", - "tif", "image/tiff", - "tiff", "image/tiff"}; + /** + * Array of file extensions and corresponding mime-types. + */ + private static final String[][] ft = { { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, { "jp2", "image/jp2" }, + { "png", "image/png" }, { "gif", "image/gif" }, + { "tif", "image/tiff" }, { "tiff", "image/tiff" }, + { "fpx", "image/fpx" }, + { "txt", "text/plain" }, { "html", "text/html" }, + { "htm", "text/html" }, { "xml", "text/xml" }, + { "svg", "image/svg+xml" }, { "meta", "text/xml" } }; + + public static Map<String, String> fileTypes; + + public static List<String> imageExtensions; + + public static List<String> textExtensions; + + public static List<String> svgExtensions; + + public static final int CLASS_NONE = -1; + + public static final int CLASS_IMAGE = 0; + + public static final int CLASS_TEXT = 1; + + public static final int CLASS_SVG = 2; + + public static final int NUM_CLASSES = 3; + + public static final Integer HINT_BASEDIRS = new Integer(1); + + public static final Integer HINT_FILEEXT = new Integer(2); + + public static final Integer HINT_DIRS = new Integer(3); + + /** + * static initializer for FileOps + */ + static { + fileTypes = new HashMap<String, String>(); + imageExtensions = new ArrayList<String>(); + textExtensions = new ArrayList<String>(); + svgExtensions = new ArrayList<String>(); + // iterate through file types in ft and fill the Map and Lists + for (int i = 0; i < ft.length; i++) { + String ext = ft[i][0]; + String mt = ft[i][1]; + fileTypes.put(ext, mt); + if (classForMimetype(mt) == CLASS_IMAGE) { + imageExtensions.add(ext); + } else if (classForMimetype(mt) == CLASS_TEXT) { + textExtensions.add(ext); + } else if (classForMimetype(mt) == CLASS_SVG) { + svgExtensions.add(ext); + } + } + } - public FileOps() { - util = new Utils(); - } + /** + * returns the file class for a mime-type + * + * @param mt + * @return + */ + public static int classForMimetype(String mt) { + if (mt == null) { + return CLASS_NONE; + } + if (mt.startsWith("image/svg")) { + return CLASS_SVG; + } else if (mt.startsWith("image")) { + return CLASS_IMAGE; + } else if (mt.startsWith("text")) { + return CLASS_TEXT; + } + return CLASS_NONE; + } + + /** + * get the mime type for a file format (by extension) + */ + public static String mimeForFile(File f) { + return (String) fileTypes.get(extname(f.getName().toLowerCase())); + } - public FileOps(Utils u) { - util = u; - } + /** + * get the file class for the filename (by extension) + * + * @param fn + * @return + */ + public static int classForFilename(String fn) { + String mt = (String) fileTypes.get(extname(fn).toLowerCase()); + return classForMimetype(mt); + } + + public static Iterator<String> getImageExtensionIterator() { + return imageExtensions.iterator(); + } - public void setUtils(Utils u) { - util = u; - } + public static List<String> getImageExtensions() { + return imageExtensions; + } + + public static Iterator<String> getTextExtensionIterator() { + return textExtensions.iterator(); + } + + public static List<String> getTextExtensions() { + return textExtensions; + } + + public static Iterator<String> getSVGExtensionIterator() { + return svgExtensions.iterator(); + } + + public static List<String> getSvgExtensions() { + return svgExtensions; + } - /** - * get the mime type for a file format (by extension) - */ - public static String mimeForFile(File f) { - String fn = f.getName(); - for (int i = 0; i < fileTypes.length; i += 2) { - if (fn.toLowerCase().endsWith(fileTypes[i])) { - return fileTypes[i+1]; - } - } - return null; - } + /** + * convert a string with a list of pathnames into an array of strings using + * the system's path separator string + */ + public static String[] pathToArray(String paths) { + // split list into directories + StringTokenizer dirs = new StringTokenizer(paths, File.pathSeparator); + int n = dirs.countTokens(); + if (n < 1) { + return null; + } + // add directories into array + String[] pathArray = new String[n]; + for (int i = 0; i < n; i++) { + String s = dirs.nextToken(); + // make shure the dir name ends with a directory separator + if (s.endsWith(File.separator)) { + pathArray[i] = s; + } else { + pathArray[i] = s + File.separator; + } + } + return pathArray; + } - /** - * get a filehandle for a file or directory name - * returns File number n if fn is directory (starts with 1) - */ - public File getFile(String fn, int n) throws FileOpException { - util.dprintln(4, "getFile ("+fn+", "+n+")"); + /** + * Extract the base of a file name (sans extension). + * + * Returns the filename without the extension. The extension is the part + * behind the last dot in the filename. If the filename has no dot the full + * file name is returned. + * + * @param fn + * @return + */ + public static String basename(String fn) { + if (fn == null) { + return null; + } + int i = fn.lastIndexOf('.'); + if (i > 0) { + return fn.substring(0, i); + } + return fn; + } - File f = new File(fn); - // if fn is a file name then return file - if (f.isFile()) { - return f; - } - // if fn is a directory name then open directory - if (f.isDirectory()) { - File[] fl = f.listFiles(new ImgFileFilter()); - Arrays.sort(fl); - if ((n > 0) && (n <= fl.length)) { - return fl[n - 1]; - } - } - throw new FileOpException("Unable to find file: "+fn); - } + /** + * Extract the extension of a file name. + * + * Returns the extension of a file name. The extension is the part behind + * the last dot in the filename. If the filename has no dot the empty string + * is returned. + * + * @param fn + * @return + */ + public static String extname(String fn) { + if (fn == null) { + return null; + } + int i = fn.lastIndexOf('.'); + if (i > 0) { + return fn.substring(i + 1); + } + return ""; + } - /** - * get the number of files in a directory - * (almost the same as getFile) - * returns 0 in case of problems - */ - public int getNumFiles(String fn) throws FileOpException { - util.dprintln(4, "getNumFiles ("+fn+")"); + /** + * Extract the parent directory of a (digilib) path name. + * + * Returns the parent directory of a path name. The parent is the part + * before the last slash in the path name. If the path name has no slash the + * empty string is returned. + * + * @param fn + * @return + */ + public static String parent(String fn) { + if (fn == null) { + return null; + } + int i = fn.lastIndexOf('/'); + if (i > 0) { + return fn.substring(0, i); + } + return ""; + } - File f = new File(fn); - // if fn is a file name then return 1 - if (f.isFile()) { - return 1; - } - // if fn is a directory name then return the number of files - if (f.isDirectory()) { - return f.listFiles(new ImgFileFilter()).length; - } - // then fn must be something strange... - return 0; - } - - - /** - * get a filehandle for a file or directory name out of a list - * dirs is a list of base directories, fn is the appended file/dirname - * searches dirs until fn exists (backwards if fwd is false) - * returns File number n if fn is directory (starts with 1) - */ - public File getFileVariant(String[] dirs, String fn, int n, boolean fwd) throws FileOpException { - util.dprintln(4, "getVariantFile ("+dirs+", "+fn+", "+n+")"); + /** + * Normalize a path name. + * + * Removes leading and trailing slashes. Returns null if there is other + * unwanted stuff in the path name. + * + * @param pathname + * @return + */ + public static String normalName(String pathname) { + if (pathname == null) { + return null; + } + // upper-dir references are unwanted + if (pathname.indexOf("../") >= 0) { + return null; + } + int a = 0; + int e = pathname.length() - 1; + if (e < 0) { + return pathname; + } + // leading and trailing "/" are removed + while ((a <= e) && (pathname.charAt(a) == '/')) { + a++; + } + while ((a < e) && (pathname.charAt(e) == '/')) { + e--; + } + return pathname.substring(a, e + 1); + } - File f = null; - int start = 0; - int inc = 1; - int end = dirs.length; - if (fwd == false) { - start = dirs.length - 1; - inc = -1; - end = 0; - } + /** + * FileFilter for general files + */ + static class ReadableFileFilter implements FileFilter { + + public boolean accept(File f) { + return f.canRead(); + } + } + + /** + * FileFilter for image types (helper class for getFile) + */ + static class ImageFileFilter implements FileFilter { + + public boolean accept(File f) { + return (classForFilename(f.getName()) == CLASS_IMAGE); + } + } + + /** + * FileFilter for text types (helper class for getFile) + */ + static class TextFileFilter implements FileFilter { + + public boolean accept(File f) { + return (classForFilename(f.getName()) == CLASS_TEXT); + } + } - for (int i = start; i != end; i += inc) { - try { - f = getFile(dirs[i]+fn, n); - } catch (FileOpException e) { - f = null; - } - if (f != null) { - return f; - } - } - throw new FileOpException("Unable to find file: "+fn); - } + /** + * FileFilter for svg types (helper class for getFile). + * + */ + static class SVGFileFilter implements FileFilter { + + public boolean accept(File f) { + return (classForFilename(f.getName()) == CLASS_SVG); + } + } - /** - * get the number of files in a directory - * (almost the same as getFileVariant) - * returns 0 in case of problems - */ - public int getNumFilesVariant(String[] dirs, String fn, boolean fwd) throws FileOpException { - util.dprintln(4, "getNumFilesVariant ("+dirs+", "+fn+")"); + /** + * Factory for FileFilters (image or text). + * + * @param fileClass + * @return + */ + public static FileFilter filterForClass(int fileClass) { + if (fileClass == CLASS_IMAGE) { + return new ImageFileFilter(); + } + if (fileClass == CLASS_TEXT) { + return new TextFileFilter(); + } + if (fileClass == CLASS_SVG) { + return new SVGFileFilter(); + } + return null; + } - int nf = 0; - int start = 0; - int inc = 1; - int end = dirs.length; - if (fwd == false) { - start = dirs.length - 1; - inc = -1; - end = 0; - } + /** + * Factory for DocuDirents based on file class. + * + * Returns an ImageFileset, TextFile or SVGFile. baseDirs and scalext are + * only for ImageFilesets. + * + * @param fileClass + * @param file + * @param hints + * optional additional parameters + * @return + */ + public static DocuDirent fileForClass(int fileClass, File file, Map<Integer,Object> hints) { + // what class of file do we have? + if (fileClass == CLASS_IMAGE) { + // image file + return new ImageFileset(file, hints); + } else if (fileClass == CLASS_TEXT) { + // text file + return new TextFile(file); + } else if (fileClass == CLASS_SVG) { + // text file + return new SVGFile(file); + } + return null; + } - for (int i = start; i != end; i += inc) { - try { - nf = getNumFiles(dirs[i]+fn); - } catch (FileOpException e) { - nf = 0; - } - if (nf > 0) { - return nf; - } - } - return 0; - } + /** + * Filters a list of Files through a FileFilter. + * + * @param files + * @param filter + * @return + */ + public static File[] listFiles(File[] files, FileFilter filter) { + if (files == null) { + return null; + } + File[] ff = new File[files.length]; + int ffi = 0; + for (int i = 0; i < files.length; i++) { + if (filter.accept(files[i])) { + ff[ffi] = files[i]; + ffi++; + } + } + File[] fff = new File[ffi]; + System.arraycopy(ff, 0, fff, 0, ffi); + return fff; + } - /** - * FileFilter for image types (helper class for getFile) - */ - private class ImgFileFilter implements FileFilter { - - public boolean accept(File f) { - if (f.isFile()) { - return (mimeForFile(f) != null); - } else { - return false; - } - } - } + /** + * Creates a new hints Map with the given first element. + * + * @param type + * @param value + * @return + */ + public static Map<Integer, Object> newHints(Integer type, Object value) { + Map<Integer, Object> m = new HashMap<Integer, Object>(); + if (type != null) { + m.put(type, value); + } + return m; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/ImageFileset.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,413 @@ +/* ImageFileset -- digilib image file info class. + * Digital Image Library servlet components + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be + * found at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package digilib.io; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import digilib.image.ImageOps; +import digilib.image.ImageSize; + +/** + * @author casties + */ +public class ImageFileset extends DocuDirent { + + /** this is an image file */ + protected static int fileClass = FileOps.CLASS_IMAGE; + + /** list of files (ImageFile) */ + private List<ImageFile> list = null; + + /** aspect ratio (width/height) */ + private float aspect = 0; + + /** resolution of the biggest image (DPI) */ + private float resX = 0; + + /** resolution of the biggest image (DPI) */ + private float resY = 0; + + /** + * Creator for empty fileset. + * + * + * @param initialCapacity + */ + public ImageFileset() { + list = new ArrayList<ImageFile>(); + } + + /** + * Constructor with a file and hints. + * + * The hints are expected to contain 'basedirs' and 'scaledfilext' keys. + * + * @param file + * @param hints + */ + public ImageFileset(File file, Map<Integer,Object> hints) { + Directory[] dirs = (Directory[]) hints.get(FileOps.HINT_BASEDIRS); + int nb = dirs.length; + list = new ArrayList<ImageFile>(nb); + parent = dirs[0]; + fill(dirs, file, hints); + } + + /** + * Adds an ImageFile to this Fileset. + * + * The files should be added in the order of higher to lower resolutions. + * The first file is considered the hires "original". + * + * + * @param f + * file to add + * @return true (always) + */ + public boolean add(ImageFile f) { + f.setParent(this); + return list.add(f); + } + + /** + * The number of image files in this Fileset. + * + * + * @return number of image files + */ + public int size() { + return (list != null) ? list.size() : 0; + } + + /** + * Gets the default File. + * + */ + public File getFile() { + return (list != null) ? list.get(0).getFile() : null; + } + + /** + * Get the ImageFile at the index. + * + * + * @param index + * @return + */ + public ImageFile get(int index) { + return list.get(index); + } + + /** + * Get the next smaller ImageFile than the given size. + * + * Returns the ImageFile from the set that has a width and height smaller or + * equal the given size. Returns null if there isn't any smaller image. + * Needs DocuInfo instance to checkFile(). + * + * + * @param size + * @param info + * @return + */ + public ImageFile getNextSmaller(ImageSize size) { + for (Iterator<ImageFile> i = getHiresIterator(); i.hasNext();) { + ImageFile f = i.next(); + try { + if (!f.isChecked()) { + ImageOps.checkFile(f); + } + if (f.getSize().isTotallySmallerThan(size)) { + return f; + } + } catch (IOException e) { + } + } + return null; + } + + /** + * Get the next bigger ImageFile than the given size. + * + * Returns the ImageFile from the set that has a width or height bigger or + * equal the given size. Returns null if there isn't any bigger image. Needs + * DocuInfo instance to checkFile(). + * + * + * @param size + * @param info + * @return + */ + public ImageFile getNextBigger(ImageSize size) { + for (ListIterator<ImageFile> i = getLoresIterator(); i.hasPrevious();) { + ImageFile f = i.previous(); + try { + if (!f.isChecked()) { + ImageOps.checkFile(f); + } + if (f.getSize().isBiggerThan(size)) { + return f; + } + } catch (IOException e) { + } + } + return null; + } + + /** + * Returns the biggest ImageFile in the set. + * + * + * @return + */ + public ImageFile getBiggest() { + return this.get(0); + } + + /** + * Returns the biggest ImageFile in the set. + * + * + * @return + */ + public ImageFile getSmallest() { + return this.get(this.size() - 1); + } + + /** + * Get an Iterator for this Fileset starting at the highest resolution + * images. + * + * + * @return + */ + public ListIterator<ImageFile> getHiresIterator() { + return list.listIterator(); + } + + /** + * Get an Iterator for this Fileset starting at the lowest resolution + * images. + * + * The Iterator starts at the last element, so you have to use it backwards + * with hasPrevious() and previous(). + * + * + * @return + */ + public ListIterator<ImageFile> getLoresIterator() { + return list.listIterator(list.size()); + } + + /** + * Fill the ImageFileset with files from different base directories. + * + * + * @param dirs + * list of base directories + * @param fl + * file (from first base dir) + * @param hints + * + */ + void fill(Directory[] dirs, File fl, Map<Integer,Object> hints) { + int nb = dirs.length; + String fn = fl.getName(); + String baseFn = FileOps.basename(fn); + // add the first ImageFile to the ImageFileset + add(new ImageFile(fn, this, parent)); + // iterate the remaining base directories + for (int dirIdx = 1; dirIdx < nb; dirIdx++) { + if (dirs[dirIdx] == null) { + continue; + } + // read the directory + if (dirs[dirIdx].getFilenames() == null) { + dirs[dirIdx].readDir(); + } + String[] dirFiles = dirs[dirIdx].getFilenames(); + // try the same filename as the original + int fileIdx = Arrays.binarySearch(dirFiles, fn); + if (fileIdx < 0) { + // try closest matches without extension + fileIdx = -fileIdx - 1; + // try idx + if ((fileIdx < dirFiles.length) + && (FileOps.basename(dirFiles[fileIdx]).equals(baseFn))) { + // idx ok + } else if ((fileIdx > 0) + && (FileOps.basename(dirFiles[fileIdx - 1]) + .equals(baseFn))) { + // idx-1 ok + fileIdx = fileIdx - 1; + } else if ((fileIdx+1 < dirFiles.length) + && (FileOps.basename(dirFiles[fileIdx + 1]) + .equals(baseFn))) { + // idx+1 ok + fileIdx = fileIdx + 1; + } else { + // basename doesn't match + continue; + } + } + if (FileOps.classForFilename(dirFiles[fileIdx]) == FileOps.CLASS_IMAGE) { + /* logger.debug("adding file " + dirFiles[fileIdx] + + " to Fileset " + this.getName()); */ + add(new ImageFile(dirFiles[fileIdx], this, dirs[dirIdx])); + } + } + } + + /** + * Checks metadata and sets resolution in resX and resY. + * + */ + public void checkMeta() { + if (metaChecked) { + return; + } + if (fileMeta == null) { + // try to read metadata file + readMeta(); + if (fileMeta == null) { + // try directory metadata + ((DocuDirectory) parent).checkMeta(); + if (((DocuDirectory) parent).getDirMeta() != null) { + fileMeta = ((DocuDirectory) parent).getDirMeta(); + } else { + // try parent directory metadata + DocuDirectory gp = (DocuDirectory) parent.getParent(); + if (gp != null) { + gp.checkMeta(); + if (gp.getDirMeta() != null) { + fileMeta = gp.getDirMeta(); + } + } + } + } + } + if (fileMeta == null) { + // no metadata available + metaChecked = true; + return; + } + metaChecked = true; + float dpi = 0; + float dpix = 0; + float dpiy = 0; + float sizex = 0; + float sizey = 0; + float pixx = 0; + float pixy = 0; + // DPI is valid for X and Y + if (fileMeta.containsKey("original-dpi")) { + try { + dpi = Float.parseFloat((String) fileMeta.get("original-dpi")); + } catch (NumberFormatException e) { + } + if (dpi != 0) { + resX = dpi; + resY = dpi; + return; + } + } + // DPI-X and DPI-Y + if (fileMeta.containsKey("original-dpi-x") + && fileMeta.containsKey("original-dpi-y")) { + try { + dpix = Float.parseFloat((String) fileMeta + .get("original-dpi-x")); + dpiy = Float.parseFloat((String) fileMeta + .get("original-dpi-y")); + } catch (NumberFormatException e) { + } + if ((dpix != 0) && (dpiy != 0)) { + resX = dpix; + resY = dpiy; + return; + } + } + // SIZE-X and SIZE-Y and PIXEL-X and PIXEL-Y + if (fileMeta.containsKey("original-size-x") + && fileMeta.containsKey("original-size-y") + && fileMeta.containsKey("original-pixel-x") + && fileMeta.containsKey("original-pixel-y")) { + try { + sizex = Float.parseFloat((String) fileMeta + .get("original-size-x")); + sizey = Float.parseFloat((String) fileMeta + .get("original-size-y")); + pixx = Float.parseFloat((String) fileMeta + .get("original-pixel-x")); + pixy = Float.parseFloat((String) fileMeta + .get("original-pixel-y")); + } catch (NumberFormatException e) { + } + if ((sizex != 0) && (sizey != 0) && (pixx != 0) && (pixy != 0)) { + resX = pixx / (sizex * 100 / 2.54f); + resY = pixy / (sizey * 100 / 2.54f); + return; + } + } + } + + /** + * @return + */ + public float getResX() { + return resX; + } + + /** + * @return + */ + public float getResY() { + return resY; + } + + /** + * Sets the aspect ratio from an ImageSize. + * + * + * @param f + */ + public void setAspect(ImageSize s) { + aspect = s.getAspect(); + } + + /** + * Returns the aspect ratio. + * + * Aspect ratio is (width/height). So it's <1 for portrait and >1 for + * landscape. + * + * + * @return + */ + public float getAspect() { + return aspect; + } + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/MetadataMap.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,14 @@ +/** + * + */ +package digilib.io; + +import java.util.HashMap; + +/** Map for metadata related to files. + * @author casties + * + */ +public class MetadataMap extends HashMap<String, String> { + +}
--- a/servlet/src/digilib/io/XMLListLoader.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/io/XMLListLoader.java Fri Feb 05 20:58:38 2010 +0100 @@ -21,121 +21,158 @@ package digilib.io; // JAXP packages -import javax.xml.parsers.*; -import org.xml.sax.*; -import org.xml.sax.helpers.*; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; -import java.util.*; -import java.io.*; +import org.apache.log4j.Logger; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; +/** Loads a simple XML list into a HashMap. + * + * The XML file has an outer <code>list_tag</code>. Every entry is an + * <code>entry_tag</code> with two attributes: the <code>key_att</code> + * key and the <code>value_att</code> value. + * + * The file is read by the <code>loadURL</code> method, that returns a + * HashMap with the key-value pairs. + * + * @author casties + */ public class XMLListLoader { - private String listTag = "list"; - private String entryTag = "entry"; - private String keyAtt = "key"; - private String valueAtt = "value"; + private Logger logger = Logger.getLogger(this.getClass()); + private String listTag = "list"; + private String entryTag = "entry"; + private String keyAtt = "key"; + private String valueAtt = "value"; - public XMLListLoader() { - } + public XMLListLoader() { + } - public XMLListLoader(String list_tag, String entry_tag, String key_att, String value_att) { - //System.out.println("xmlListLoader("+list_tag+","+entry_tag+","+key_att+","+value_att+")"); - listTag = list_tag; - entryTag = entry_tag; - keyAtt = key_att; - valueAtt = value_att; - } - - /** - * inner class XMLListParser to be called by the parser - */ - private class XMLListParser extends DefaultHandler { + public XMLListLoader( + String list_tag, + String entry_tag, + String key_att, + String value_att) { + logger.debug("xmlListLoader("+list_tag+","+entry_tag+","+key_att+","+value_att+")"); + listTag = list_tag; + entryTag = entry_tag; + keyAtt = key_att; + valueAtt = value_att; + } - private Hashtable listData; - private Stack nameSpace; + /** + * inner class XMLListParser to be called by the parser + */ + private class XMLListParser extends DefaultHandler { - public Hashtable getData() { - return listData; - } + private Map<String, String> listData; + private LinkedList<String> tagSpace; + + public Map<String, String> getData() { + return listData; + } - // Parser calls this once at the beginning of a document - public void startDocument() throws SAXException { - listData = new Hashtable(); - nameSpace = new Stack(); - } + // Parser calls this once at the beginning of a document + public void startDocument() throws SAXException { + listData = new HashMap<String, String>(); + tagSpace = new LinkedList<String>(); + } - // Parser calls this for each element in a document - public void startElement(String namespaceURI, String localName, - String qName, Attributes atts) - throws SAXException - { - //System.out.println("<"+qName); - // open a new namespace - nameSpace.push(qName); + // Parser calls this for each element in a document + public void startElement( + String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + //System.out.println("<"+qName); + // open a new namespace + tagSpace.addLast(qName); - // ist it an entry tag? - if (qName.equals(entryTag)) { - // is it inside a list tag? - if ((listTag.length() > 0)&&(nameSpace.search(listTag) < 0)) { - System.out.println("BOO: Entry "+entryTag+" not inside list "+listTag); - throw new SAXParseException("Entry "+entryTag+" not inside list "+listTag, null); - } - // get the attributes - String key = atts.getValue(keyAtt); - String val = atts.getValue(valueAtt); - if ((key == null)||(val == null)) { - System.out.println("BOO: Entry "+entryTag+" does not have Attributes "+keyAtt+", "+valueAtt); - throw new SAXParseException("Entry "+entryTag+" does not have Attributes "+keyAtt+", "+valueAtt, null); - } - // add the values - //System.out.println("DATA: "+key+" = "+val); - listData.put(key, val); - } - } - - public void endElement(String namespaceURI, String localName, - String qName) - throws SAXException - { - // exit the namespace - nameSpace.pop(); - } - - } - + // ist it an entry tag? + if (qName.equals(entryTag)) { + // is it inside a list tag? + if ((listTag.length() > 0) && (!tagSpace.contains(listTag))) { + logger.error("BOO: Entry " + + entryTag + + " not inside list " + + listTag); + throw new SAXParseException( + "Entry " + entryTag + " not inside list " + listTag, + null); + } + // get the attributes + String key = atts.getValue(keyAtt); + String val = atts.getValue(valueAtt); + if ((key == null) || (val == null)) { + logger.error("BOO: Entry " + + entryTag + + " does not have Attributes " + + keyAtt + + ", " + + valueAtt); + throw new SAXParseException( + "Entry " + + entryTag + + " does not have Attributes " + + keyAtt + + ", " + + valueAtt, + null); + } + // add the values + //System.out.println("DATA: "+key+" = "+val); + listData.put(key, val); + } + } - /** - * load and parse a file (as URL) - * returns Hashtable with list data - */ - public Hashtable loadURL(String path) throws SAXException, IOException { - //System.out.println("loadurl ("+path+")"); - // Create a JAXP SAXParserFactory and configure it - SAXParserFactory spf = SAXParserFactory.newInstance(); - //spf.setNamespaceAware(true); + public void endElement( + String namespaceURI, + String localName, + String qName) + throws SAXException { + // exit the namespace + tagSpace.removeLast(); + } - XMLReader xmlReader = null; - try { - // Create a JAXP SAXParser - SAXParser saxParser = spf.newSAXParser(); + } - // Get the encapsulated SAX XMLReader - xmlReader = saxParser.getXMLReader(); - } - catch (ParserConfigurationException e) { - throw new SAXException(e); - } + /** + * load and parse a file (as URL) + * returns HashMap with list data + */ + public Map<String, String> loadURL(String path) throws SAXException, IOException { + //System.out.println("loadurl ("+path+")"); + // Create a JAXP SAXParserFactory and configure it + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); - // create a list parser (keeps the data!) - XMLListParser listParser = new XMLListParser(); + SAXParser parser = null; + try { + // Create a JAXP SAXParser + parser = spf.newSAXParser(); - // Set the ContentHandler of the XMLReader - xmlReader.setContentHandler(listParser); + } catch (ParserConfigurationException e) { + throw new SAXException(e); + } - // Tell the XMLReader to parse the XML document - xmlReader.parse(path); + // create a list parser (keeps the data!) + XMLListParser listParser = new XMLListParser(); - return listParser.getData(); - } + // Tell the SAXParser to parse the XML document + parser.parse(path, listParser); + + return listParser.getData(); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/XMLMetaLoader.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,287 @@ +/* XMLMetaLoader -- Load an XML format metadata into a Hashtable + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +package digilib.io; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.log4j.Logger; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +public class XMLMetaLoader { + + private Logger logger = Logger.getLogger(this.getClass()); + private String metaTag = "meta"; + private String fileTag = "file"; + private String fileNameTag = "name"; + private String filePathTag = "path"; + private String imgTag = "img"; + private String collectTag = "context"; + + public XMLMetaLoader() { + } + + /** + * inner class XMLMetaParser to be called by the parser + */ + private class XMLMetaParser extends DefaultHandler { + + private LinkedList<String> tags; + private Map<String, MetadataMap> files; + private MetadataMap meta; + private StringBuffer content; + private boolean collecting; + private StringBuffer collectedContent; + private String fileName; + private String filePath; + + /** + * extracts the elements name from either localName ln or qName qn. + * + * @param ln localName + * @param qn qName + * @return element name + */ + private String getName(String ln, String qn) { + if (ln != null) { + if (ln.length() > 0) { + return ln; + } + } + // else it's qName (or nothing) + return qn; + } + + /** + * returns all attributes as a String + * + * @param attrs + * @return + */ + private String getAttrString(Attributes attrs) { + StringBuffer s = new StringBuffer(); + for (int i = 0; i < attrs.getLength(); i++) { + String key = getName(attrs.getLocalName(i), attrs.getQName(i)); + s.append(" "+key+"=\""+attrs.getValue(i)+"\""); + } + return s.toString(); + } + + + // Parser calls this once at the beginning of a document + public void startDocument() throws SAXException { + tags = new LinkedList<String>(); + files = new HashMap<String, MetadataMap>(); + collecting = false; + collectedContent = null; + } + + // Parser calls this for each element in a document + public void startElement( + String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + String name = getName(localName, qName); + // open a new tag + tags.addLast(name); + // start new content (no nesting of tags and content) + content = new StringBuffer(); + + if (name.equals(metaTag)) { + // new meta tag + meta = new MetadataMap(); + collectedContent = new StringBuffer(); + } else if (name.equals(fileTag)) { + // new file tag + fileName = null; + filePath = null; + meta = new MetadataMap(); + collectedContent = new StringBuffer(); + } else if (name.equals(collectTag)) { + // start collecting + collecting = true; + if (collectedContent == null) { + collectedContent = new StringBuffer(); + } + } + + // record mode + if (collecting) { + collectedContent.append("<"+name); + collectedContent.append(getAttrString(atts)); + collectedContent.append(">"); + } + } + + // parser calls this for all tag content (possibly more than once) + public void characters(char[] ch, int start, int length) + throws SAXException { + // append data to current string buffer + if (content == null) { + content = new StringBuffer(); + } + content.append(ch, start, length); + } + + // parser calls this at the end of each element + public void endElement( + String namespaceURI, + String localName, + String qName) + throws SAXException { + + String name = getName(localName, qName); + // exit the tag + tags.removeLast(); + String lastTag = (tags.isEmpty()) ? "" : tags.getLast(); + + // was it a file/name tag? + if (name.equals(fileNameTag) && lastTag.equals(fileTag)) { + // save name as filename + if ((content != null) && (content.length() > 0)) { + fileName = content.toString().trim(); + } + content = null; + return; + } + + // was it a file/path tag? + if (name.equals(filePathTag) && lastTag.equals(fileTag)) { + // save path as filepath + if ((content != null) && (content.length() > 0)) { + filePath = content.toString().trim(); + } + content = null; + return; + } + + // was it a file tag? + if (name.equals(fileTag)) { + // is there meta to save? + if ((meta != null) && (meta.size() > 0)) { + // file name is (optional file/path) / file/name + String fn = null; + + if (fileName != null) { + if (filePath != null) { + fn = filePath + "/" + fileName; + } else { + fn = fileName; + } + } else { + // no file name, no file + content = null; + return; + } + // save meta in file list + files.put(fn, meta); + } + content = null; + return; + } + + // was it a meta tag outside a file tag? + if (name.equals(metaTag) && !tags.contains(fileTag)) { + // save meta as dir meta + if ((meta != null) && (meta.size() > 0)) { + files.put("", meta); + } + content = null; + return; + } + + // is this inside an digilib info (=img) tag? + if (lastTag.equals(imgTag)) { + // then add whatever this is + if ((content != null) && (content.length() > 0)) { + meta.put(name, content.toString().trim()); + } + content = null; + return; + } + + // is this the end of collectTag? + if (name.equals(collectTag)) { + collecting = false; + collectedContent.append("</"+collectTag+">\n"); + // store collected stuff + meta.put(collectTag, collectedContent.toString()); + //logger.debug("collected: '"+collectedContent+"'"); + content = null; + return; + } + + // write collected content + if (collecting) { + String s = ""; + if ((content != null) && (content.length() > 0)) { + s = content.toString().trim(); + } + //logger.debug("collect:"+name+" = "+s); + collectedContent.append(s); + collectedContent.append("</"+name+">\n"); + content = null; + return; + } + } + + } + + /** + * load and parse a file (as URL) + * returns HashMap with list data + */ + public Map<String, MetadataMap> loadURL(String path) throws SAXException, IOException { + logger.debug("loading meta: "+path); + // Create a JAXP SAXParserFactory and configure it + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + + SAXParser parser = null; + try { + // Create a JAXP SAXParser + parser = spf.newSAXParser(); + + } catch (ParserConfigurationException e) { + throw new SAXException(e); + } + + // create a list parser (keeps the data!) + XMLMetaParser listParser = new XMLMetaParser(); + + // Tell the SAXParser to parse the XML document + parser.parse(path, listParser); + + return listParser.files; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibConfiguration.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,265 @@ +/* + * DigilibConfiguration -- Holding all parameters for digilib servlet. + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +package digilib.servlet; + +import java.io.File; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Logger; + +import digilib.image.DocuImage; +import digilib.image.DocuImageImpl; +import digilib.io.FileOps; +import digilib.io.XMLListLoader; + +/** + * Class to hold the digilib servlet configuration parameters. The parameters + * can be read from the digilib-config file and be passed to other servlets or + * beans. <br>errorImgFileName: image file to send in case of error. <br> + * denyImgFileName: image file to send if access is denied. <br>baseDirs: + * array of base directories in order of preference (prescaled versions first). + * <br>useAuth: use authentication information. <br>authConfPath: + * authentication configuration file. <br>... <br> + * + * @author casties + * + */ +public class DigilibConfiguration extends ParameterMap { + + private static final long serialVersionUID = -6630487070791637120L; + + /** DocuImage class instance */ + private Class<DocuImageImpl> docuImageClass = null; + + /** Log4J logger */ + private Logger logger = Logger.getLogger("digilib.config"); + + /** + * Default constructor defines all parameters and their default values. + * + */ + public DigilibConfiguration() { + // create HashMap(20) + super(20); + // we start with a default logger config + BasicConfigurator.configure(); + + /* + * Definition of parameters and default values. System parameters that + * are not read from config file have a type 's'. + */ + + // digilib servlet version + newParameter( + "servlet.version", + digilib.servlet.Scaler.getVersion(), + null, + 's'); + // configuration file location + newParameter("servlet.config.file", null, null, 's'); + // DocuDirCache instance + newParameter("servlet.dir.cache", null, null, 's'); + // DocuImage class instance + newParameter( + "servlet.docuimage.class", + digilib.image.JAIDocuImage.class, + null, + 's'); + // AuthOps instance for authentication + newParameter("servlet.auth.op", null, null, 's'); + + /* + * parameters that can be read from config file have a type 'f' + */ + + // image file to send in case of error + newParameter( + "error-image", + new File("img/digilib-error.png"), + null, + 'f'); + // image file to send if access is denied + newParameter( + "denied-image", + new File("img/digilib-denied.png"), + null, + 'f'); + // image file to send if image file not found + newParameter( + "notfound-image", + new File("img/digilib-notfound.png"), + null, + 'f'); + // base directories in order of preference (prescaled versions last) + String[] bd = { "/docuserver/images", "/docuserver/scaled/small" }; + newParameter("basedir-list", bd, null, 'f'); + // use authentication information + newParameter("use-authorization", Boolean.FALSE, null, 'f'); + // authentication configuration file + newParameter("auth-file", new File("digilib-auth.xml"), null, 'f'); + // sending image files as-is allowed + newParameter("sendfile-allowed", Boolean.TRUE, null, 'f'); + // Debug level + newParameter("debug-level", new Integer(5), null, 'f'); + // Type of DocuImage instance + newParameter( + "docuimage-class", + "digilib.image.JAIDocuImage", + null, + 'f'); + // part of URL used to indicate authorized access + newParameter("auth-url-path", "authenticated/", null, 'f'); + // degree of subsampling on image load + newParameter("subsample-minimum", new Float(2f), null, 'f'); + // default scaling quality + newParameter("default-quality", new Integer(1), null, 'f'); + // use mapping file to translate paths + newParameter("use-mapping", Boolean.FALSE, null, 'f'); + // mapping file location + newParameter("mapping-file", new File("digilib-map.xml"), null, 'f'); + // log4j config file location + newParameter("log-config-file", new File("log4j-config.xml"), null, 'f'); + // maximum destination image size (0 means no limit) + newParameter("max-image-size", new Integer(0), null, 'f'); + // use safe (but slower) directory indexing + newParameter("safe-dir-index", Boolean.FALSE, null, 'f'); + // number of working threads + newParameter("worker-threads", new Integer(1), null, 'f'); + // max number of waiting threads + newParameter("max-waiting-threads", new Integer(0), null, 'f'); + + } + + /** + * Constructor taking a ServletConfig. Reads the config file location from + * an init parameter and loads the config file. Calls <code>readConfig()</code>. + * + * @see readConfig() + */ + public DigilibConfiguration(ServletConfig c) throws Exception { + this(); + readConfig(c); + } + + /** + * read parameter list from the XML file in init parameter "config-file" + */ + public void readConfig(ServletConfig c) throws Exception { + + /* + * Get config file name. The file name is first looked for as an init + * parameter, then in a fixed location in the webapp. + */ + if (c == null) { + // no config no file... + return; + } + String fn = c.getInitParameter("config-file"); + if (fn == null) { + fn = ServletOps.getConfigFile("digilib-config.xml", c); + if (fn == null) { + logger.fatal("readConfig: no param config-file"); + throw new ServletException("ERROR: no digilib config file!"); + } + } + File f = new File(fn); + // setup config file list reader + XMLListLoader lilo = + new XMLListLoader("digilib-config", "parameter", "name", "value"); + // read config file into HashMap + Map<String,String> confTable = lilo.loadURL(f.toURL().toString()); + + // set config file path parameter + setValue("servlet.config.file", f.getCanonicalPath()); + + /* + * read parameters + */ + + for (Entry<String, String> confEntry: confTable.entrySet()) { + Parameter p = get(confEntry.getKey()); + if (p != null) { + if (p.getType() == 's') { + // type 's' Parameters are not overwritten. + continue; + } + if (!p.setValueFromString(confEntry.getValue())) { + /* + * automatic conversion failed -- try special cases + */ + + // basedir-list + if (confEntry.getKey().equals("basedir-list")) { + // split list into directories + String[] sa = FileOps.pathToArray(confEntry.getValue()); + if (sa != null) { + p.setValue(sa); + } + } + + } + } else { + // parameter unknown -- just add + newParameter(confEntry.getKey(), null, confEntry.getValue(), 'f'); + } + } + + } + + /** + * Creates a new DocuImage instance. + * + * The type of DocuImage is specified by docuImageType. + * + * @return DocuImage + */ + @SuppressWarnings("unchecked") + public DocuImage getDocuImageInstance() { + DocuImageImpl di = null; + try { + if (docuImageClass == null) { + docuImageClass = (Class<DocuImageImpl>) Class.forName(getAsString("docuimage-class")); + } + di = docuImageClass.newInstance(); + } catch (Exception e) { + } + return di; + } + + /** + * @return Returns the docuImageClass. + */ + public Class<DocuImageImpl> getDocuImageClass() { + return docuImageClass; + } + /** + * @param docuImageClass The docuImageClass to set. + */ + public void setDocuImageClass(Class<DocuImageImpl> docuImageClass) { + this.docuImageClass = docuImageClass; + } +}
--- a/servlet/src/digilib/servlet/DigilibImageWorker.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/DigilibImageWorker.java Fri Feb 05 20:58:38 2010 +0100 @@ -23,12 +23,9 @@ import java.awt.Rectangle; import java.awt.geom.Rectangle2D; -import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; -import javax.servlet.http.HttpServletResponse; - import digilib.image.DocuImage; import digilib.image.ImageOpException; import digilib.image.ImageOps;
--- a/servlet/src/digilib/servlet/DigilibInfoReader.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/DigilibInfoReader.java Fri Feb 05 20:58:38 2010 +0100 @@ -21,7 +21,7 @@ protected static Logger logger = Logger.getLogger("digilib.servlet"); private String filename = null; - private static String base_element = "info"; + //private static String base_element = "info"; public DigilibInfoReader(String fn){ filename = fn; @@ -33,16 +33,17 @@ * @param attr * @return */ - public String getAsString(String attr){ + @SuppressWarnings("unchecked") // Element.getChildren() returns naked List + public String getAsString(String attr){ try{ SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(new File(filename)); Element root = doc.getRootElement(); - List mainElements = root.getChildren(); + List<Element> mainElements = root.getChildren(); // logger.debug("XML mainElements:"+mainElements.toString()); for(int i=0; i<mainElements.size(); i++){ - Element elem = (Element) mainElements.get(i); + Element elem = mainElements.get(i); if(elem.getName()==attr){ // logger.debug(attr+" == "+(String)elem.getTextTrim()); return (String)elem.getTextTrim(); @@ -64,7 +65,7 @@ public boolean hasInfo(){ try { SAXBuilder builder = new SAXBuilder(); - Document doc = builder.build(new File(filename)); + builder.build(new File(filename)); return true; } catch(Exception e){
--- a/servlet/src/digilib/servlet/DigilibRequest.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/DigilibRequest.java Fri Feb 05 20:58:38 2010 +0100 @@ -26,19 +26,14 @@ package digilib.servlet; -import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Iterator; import java.util.StringTokenizer; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; - import digilib.image.DocuImage; import digilib.io.FileOps; @@ -68,11 +63,7 @@ private static final long serialVersionUID = -4707707539569977901L; - private final static String ECHO = "http://echo.unibe.ch/digilib/rdf#"; - - private final static String DIGILIB = "Digilib"; - - private Logger logger = Logger.getLogger(this.getClass()); + //private Logger logger = Logger.getLogger(this.getClass()); private boolean boolRDF = false; // use RDF Parameters @@ -337,8 +328,7 @@ public String getAsString(int type) { StringBuffer s = new StringBuffer(50); // go through all values - for (Iterator i = this.values().iterator(); i.hasNext();) { - Parameter p = (Parameter) i.next(); + for (Parameter p: this.values()) { if ((type > 0) && (p.getType() != type)) { // skip the wrong types continue; @@ -394,10 +384,11 @@ * @param request * ServletRequest to get parameters from. */ - public void setWithParamRequest(ServletRequest request) { + @SuppressWarnings("unchecked") // ServletRequest.getParameterNames() returns naked Enumeration + public void setWithParamRequest(ServletRequest request) { setValue("servlet.request", request); // go through all request parameters - for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) { + for (Enumeration<String> i = request.getParameterNames(); i.hasMoreElements();) { String name = (String) i.nextElement(); // is this a known parameter? if (this.containsKey(name)) {
--- a/servlet/src/digilib/servlet/DocumentBean.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/DocumentBean.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,198 +1,314 @@ -/* DocumentBean -- Access control bean for JSP - - Digital Image Library servlet components - - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ +/* + * DocumentBean -- Access control bean for JSP + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package digilib.servlet; +import java.util.List; -import java.util.*; -import javax.servlet.*; -import javax.servlet.http.*; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; -import digilib.*; -import digilib.io.*; -import digilib.auth.*; +import digilib.auth.AuthOpException; +import digilib.auth.AuthOps; +import digilib.image.ImageOps; +import digilib.image.ImageSize; +import digilib.io.DocuDirCache; +import digilib.io.DocuDirectory; +import digilib.io.FileOps; +import digilib.io.ImageFile; +import digilib.io.ImageFileset; -public class DocumentBean implements AuthOps { +public class DocumentBean { + + // general logger + private static Logger logger = Logger.getLogger("digilib.docubean"); + + // AuthOps object to check authorization + private AuthOps authOp; - // Utils object for logging - private Utils util = new Utils(5); - // AuthOps object to check authorization - private AuthOps authOp; - // FileOps object - private FileOps fileOp = new FileOps(util); + // use authorization database + private boolean useAuthentication = true; + + // path to add for authenticated access + private String authURLPath = ""; + + // DocuDirCache + private DocuDirCache dirCache = null; + + // DigilibConfiguration object + private DigilibConfiguration dlConfig; + + // DigilibRequest object + private DigilibRequest dlRequest = null; - // base directories in order of preference (prescaled versions first) - private String[] baseDirs = {"/docuserver/scaled/small", "/docuserver/images", "/docuserver/scans/quellen"}; - // part of URL path to prepend for authenticated access - private String authURLpath = "authenticated/"; + /** + * Constructor for DocumentBean. + */ + public DocumentBean() { + logger.debug("new DocumentBean"); + } - - public DocumentBean() { - } + public DocumentBean(ServletConfig conf) { + logger.debug("new DocumentBean"); + try { + setConfig(conf); + } catch (Exception e) { + logger.fatal("ERROR: Unable to read config: ", e); + } + } - public void setConfig(ServletConfig conf) throws ServletException { - util.dprintln(10, "setConfig"); - // servletOps takes a ServletConfig to get the config file name - ServletOps servletOp = new ServletOps(util, conf); - /** - * basedir-list : List of document directories - */ - String bl = servletOp.tryToGetInitParam("basedir-list", null); - if ((bl != null)&&(bl.length() > 0)) { - // split list into directories - StringTokenizer dirs = new StringTokenizer(bl, ":"); - int n = dirs.countTokens(); - if (n > 0) { - // add directories into array - baseDirs = new String[n]; - for (int i = 0; i < n; i++) { - baseDirs[i] = dirs.nextToken(); - } - } - util.dprintln(3, "basedir-list: "+bl); - } - /** - * auth-url-path : part of URL to indicate authenticated access - */ - String au = servletOp.tryToGetInitParam("auth-url-path", null); - if ((au != null)&&(au.length() > 0)) { - authURLpath = au; - util.dprintln(3, "auth-url-path: "+au); - } - /** - * authentication - */ - try { - // DB version - //private AuthOps authOp = new DBAuthOpsImpl(util); - // XML version - String cp = servletOp.tryToGetInitParam("auth-file", "/docuserver/www/digitallibrary/WEB-INF/digilib-auth.xml"); - util.dprintln(3, "auth-file: "+cp); - authOp = new XMLAuthOps(util, cp); - } catch (AuthOpException e) { - throw new ServletException(e); - } - } + public void setConfig(ServletConfig conf) throws ServletException { + logger.debug("setConfig"); + // get our ServletContext + ServletContext context = conf.getServletContext(); + // see if there is a Configuration instance + dlConfig = (DigilibConfiguration) context + .getAttribute("digilib.servlet.configuration"); + if (dlConfig == null) { + // create new Configuration + throw new ServletException("ERROR: No configuration!"); + } + + // get cache + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + + /* + * authentication + */ + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + authURLPath = dlConfig.getAsString("auth-url-path"); + if (useAuthentication && (authOp == null)) { + throw new ServletException( + "ERROR: use-authorization configured but no AuthOp!"); + } + } + + /** + * check if the request must be authorized to access filepath + */ + public boolean isAuthRequired(DigilibRequest request) + throws AuthOpException { + logger.debug("isAuthRequired"); + return useAuthentication ? authOp.isAuthRequired(request) : false; + } + + /** + * check if the request is allowed to access filepath + */ + public boolean isAuthorized(DigilibRequest request) throws AuthOpException { + logger.debug("isAuthorized"); + return useAuthentication ? authOp.isAuthorized(request) : true; + } + + /** + * return a list of authorization roles needed for request to access the + * specified path + */ + public List<String> rolesForPath(DigilibRequest request) throws AuthOpException { + logger.debug("rolesForPath"); + return useAuthentication ? authOp.rolesForPath(request) : null; + } + + /** + * check request authorization against a list of roles + */ + public boolean isRoleAuthorized(List<String> roles, DigilibRequest request) { + logger.debug("isRoleAuthorized"); + return useAuthentication ? authOp.isRoleAuthorized(roles, request) + : true; + } + + /** + * check for authenticated access and redirect if necessary + */ + public boolean doAuthentication(HttpServletResponse response) + throws Exception { + logger.debug("doAuthenication-Method"); + return doAuthentication(dlRequest, response); + } - public String getDocuPath(HttpServletRequest request) { - util.dprintln(10, "getDocuPath"); - // fetch query string - String qs = request.getQueryString(); - String fn = ""; - if (qs != null && qs.length() > 0) { - // the file name is in the request before the first "+" - int endfn = qs.indexOf("+"); - if (endfn > 0) { - fn = qs.substring(0, endfn); - } else { - fn = qs; - } - } - util.dprintln(4, "docuPath: "+fn); - return fn; - } + /** + * check for authenticated access and redirect if necessary + */ + public boolean doAuthentication(DigilibRequest request, + HttpServletResponse response) throws Exception { + logger.debug("doAuthentication"); + if (!useAuthentication) { + // shortcut if no authentication + return true; + } + // check if we are already authenticated + if (((HttpServletRequest) request.getServletRequest()).getRemoteUser() == null) { + logger.debug("unauthenticated so far"); + // if not maybe we must? + if (isAuthRequired(request)) { + logger.debug("auth required, redirect"); + // we are not yet authenticated -> redirect + response.sendRedirect(authURLPath + + ((HttpServletRequest) request.getServletRequest()) + .getServletPath() + + "?" + + ((HttpServletRequest) request.getServletRequest()) + .getQueryString()); + } + } + return true; + } - /** - * check if the request must be authorized to access filepath - */ - public boolean isAuthRequired(HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthRequired"); - return authOp.isAuthRequired(getDocuPath(request), request); - } - - public boolean isAuthRequired(String filepath, HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthRequired"); - return authOp.isAuthRequired(filepath, request); - } + /** + * Sets the current DigilibRequest. Also completes information in the request. + * + * @param dlRequest + * The dlRequest to set. + */ + public void setRequest(DigilibRequest dlRequest) throws Exception { + this.dlRequest = dlRequest; + if (dirCache == null) { + return; + } + String fn = dlRequest.getFilePath(); + // get information about the file + ImageFileset fileset = (ImageFileset) dirCache.getFile(fn, dlRequest + .getAsInt("pn"), FileOps.CLASS_IMAGE); + if (fileset == null) { + return; + } + // add file name + dlRequest.setValue("img.fn", fileset.getName()); + // add dpi + dlRequest.setValue("img.dpix", new Double(fileset.getResX())); + dlRequest.setValue("img.dpiy", new Double(fileset.getResY())); + // get number of pages in directory + DocuDirectory dd = dirCache.getDirectory(fn); + if (dd != null) { + // add pt + dlRequest.setValue("pt", dd.size()); + } + // get original pixel size + ImageFile origfile = fileset.getBiggest(); + // check image for size if mo=hires + if ((! origfile.isChecked())&&dlRequest.hasOption("mo", "hires")) { + logger.debug("pre-checking image!"); + ImageOps.checkFile(origfile); + } + ImageSize pixsize = origfile.getSize(); + if (pixsize != null) { + // add pixel size + dlRequest.setValue("img.pix_x", new Integer(pixsize.getWidth())); + dlRequest.setValue("img.pix_y", new Integer(pixsize.getHeight())); + } + } - /** - * check if the request is allowed to access filepath - */ - public boolean isAuthorized(HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthorized"); - return authOp.isAuthorized(getDocuPath(request), request); - } - - public boolean isAuthorized(String filepath, HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthorized"); - return authOp.isAuthorized(filepath, request); - } + /** + * get the first page number in the directory (not yet functional) + */ + public int getFirstPage(DigilibRequest request) { + logger.debug("getFirstPage"); + return 1; + } - /** - * return a list of authorization roles needed for request - * to access the specified path - */ - public List rolesForPath(String filepath, HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "rolesForPath"); - return authOp.rolesForPath(filepath, request); - } + /** + * get the number of pages/files in the directory + */ + public int getNumPages() throws Exception { + return getNumPages(dlRequest); + } + + /** + * get the number of pages/files in the directory + */ + public int getNumPages(DigilibRequest request) throws Exception { + logger.debug("getNumPages"); + DocuDirectory dd = (dirCache != null) ? dirCache.getDirectory(request + .getFilePath()) : null; + if (dd != null) { + return dd.size(); + } + return 0; + } - /** - * check request authorization against a list of roles - */ - public boolean isRoleAuthorized(List roles, HttpServletRequest request) { - util.dprintln(10, "isRoleAuthorized"); - return authOp.isRoleAuthorized(roles, request); - } + /** + * Returns the dlConfig. + * + * @return DigilibConfiguration + */ + public DigilibConfiguration getDlConfig() { + return dlConfig; + } + + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveRight() { + float ww = dlRequest.getAsFloat("ww"); + float wx = dlRequest.getAsFloat("wx"); + return (ww + wx < 1.0); + } - /** - * check for authenticated access and redirect if necessary - */ - public boolean doAuthentication(HttpServletRequest request, HttpServletResponse response) throws Exception { - util.dprintln(10, "doAuthentication"); - // check if we are already authenticated - if (request.getRemoteUser() == null) { - util.dprintln(3, "unauthenticated so far"); - // if not maybe we must? - if (isAuthRequired(request)) { - util.dprintln(3, "auth required, redirect"); - // we are not yet authenticated -> redirect - response.sendRedirect(authURLpath+request.getServletPath()+"?"+request.getQueryString()); - } - } - return true; - } + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveLeft() { + float ww = dlRequest.getAsFloat("ww"); + float wx = dlRequest.getAsFloat("wx"); + return ((ww < 1.0) && (wx > 0)); + } - /** - * get the first page number in the directory - * (not yet functional) - */ - public int getFirstPage(HttpServletRequest request) { - return getFirstPage(getDocuPath(request), request); - } + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveUp() { + float wh = dlRequest.getAsFloat("wh"); + float wy = dlRequest.getAsFloat("wy"); + return ((wh < 1.0) && (wy > 0)); + } - public int getFirstPage(String filepath, HttpServletRequest request) { - util.dprintln(10, "getFirstPage"); - return 1; - } + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveDown() { + float wh = dlRequest.getAsFloat("wh"); + float wy = dlRequest.getAsFloat("wy"); + return (wh + wy < 1.0); + } - /** - * get the number of pages/files in the directory - */ - public int getNumPages(HttpServletRequest request) throws Exception { - return getNumPages(getDocuPath(request), request); - } - - public int getNumPages(String filepath, HttpServletRequest request) throws Exception { - util.dprintln(10, "getNumPages"); - return fileOp.getNumFilesVariant(baseDirs, "/"+filepath, true); - } + /** + * @return Returns the dlRequest. + */ + public DigilibRequest getRequest() { + return dlRequest; + } }
--- a/servlet/src/digilib/servlet/ImageJobInformation.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/ImageJobInformation.java Fri Feb 05 20:58:38 2010 +0100 @@ -6,7 +6,6 @@ import java.io.IOException; import java.util.StringTokenizer; -import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; @@ -15,7 +14,6 @@ import digilib.image.ImageOps; import digilib.image.ImageSize; import digilib.io.DocuDirCache; -import digilib.io.DocuDirent; import digilib.io.FileOpException; import digilib.io.FileOps; import digilib.io.ImageFile; @@ -319,7 +317,7 @@ float sx = getAsFloat("ddpix") / origResX; float sy = getAsFloat("ddpiy") / origResY; // currently only same scale :-( - put("scale", (sx + sy)/2f); + setValue("scale", (sx + sy)/2f); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/Initialiser.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,161 @@ +/* Initialiser.java -- initalisation servlet for setup tasks + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 18.10.2004 + */ +package digilib.servlet; + +import java.io.File; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; + +import org.apache.log4j.Logger; +import org.apache.log4j.xml.DOMConfigurator; + +import digilib.auth.AuthOps; +import digilib.auth.XMLAuthOps; +import digilib.image.DocuImage; +import digilib.image.ImageOps; +import digilib.io.AliasingDocuDirCache; +import digilib.io.DocuDirCache; +import digilib.io.FileOps; + +/** + * Initalisation servlet for setup tasks. + * + * @author casties + * + */ +public class Initialiser extends HttpServlet { + + private static final long serialVersionUID = -5126621114382549343L; + + /** servlet version */ + public static final String iniVersion = "0.1b2"; + + /** gengeral logger for this class */ + private static Logger logger = Logger.getLogger("digilib.init"); + + /** AuthOps instance */ + AuthOps authOp; + + /** DocuDirCache instance */ + DocuDirCache dirCache; + + /** DigilibConfiguration instance */ + DigilibConfiguration dlConfig; + + /** use authorization database */ + boolean useAuthentication = false; + + /** + * Initialisation on first run. + * + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + super.init(config); + + System.out + .println("***** Digital Image Library Initialisation Servlet (version " + + iniVersion + ") *****"); + + // get our ServletContext + ServletContext context = config.getServletContext(); + // see if there is a Configuration instance + dlConfig = (DigilibConfiguration) context + .getAttribute("digilib.servlet.configuration"); + if (dlConfig == null) { + // create new Configuration + try { + dlConfig = new DigilibConfiguration(config); + + /* + * further initialization + */ + + // set up the logger + File logConf = ServletOps.getConfigFile((File) dlConfig + .getValue("log-config-file"), config); + DOMConfigurator.configure(logConf.getAbsolutePath()); + dlConfig.setValue("log-config-file", logConf); + // say hello in the log file + logger + .info("***** Digital Image Library Initialisation Servlet (version " + + iniVersion + ") *****"); + // directory cache + String[] bd = (String[]) dlConfig.getValue("basedir-list"); + int[] fcs = { FileOps.CLASS_IMAGE, FileOps.CLASS_TEXT, + FileOps.CLASS_SVG }; + if (dlConfig.getAsBoolean("use-mapping")) { + // with mapping file + File mapConf = ServletOps.getConfigFile((File) dlConfig + .getValue("mapping-file"), config); + dirCache = new AliasingDocuDirCache(bd, fcs, mapConf, + dlConfig); + dlConfig.setValue("mapping-file", mapConf); + } else { + // without mapping + dirCache = new DocuDirCache(bd, fcs, dlConfig); + } + dlConfig.setValue("servlet.dir.cache", dirCache); + // useAuthentication + if (dlConfig.getAsBoolean("use-authorization")) { + // DB version + //authOp = new DBAuthOpsImpl(util); + // XML version + File authConf = ServletOps.getConfigFile((File) dlConfig + .getValue("auth-file"), config); + authOp = new XMLAuthOps(authConf); + dlConfig.setValue("servlet.auth.op", authOp); + dlConfig.setValue("auth-file", authConf); + } + // DocuImage class + DocuImage di = dlConfig.getDocuImageInstance(); + dlConfig.setValue("servlet.docuimage.class", di.getClass().getName()); + ImageOps.setDocuImage(di); + // worker threads + int nt = dlConfig.getAsInt("worker-threads"); + DigilibWorker.setSemaphore(nt, true); + int mt = dlConfig.getAsInt("max-waiting-threads"); + DigilibWorker.setMaxWaitingThreads(mt); + // set as the servlets main config + context.setAttribute("digilib.servlet.configuration", dlConfig); + + } catch (Exception e) { + throw new ServletException(e); + } + } else { + // say hello in the log file + logger + .info("***** Digital Image Library Initialisation Servlet (version " + + iniVersion + ") *****"); + logger.warn("Already initialised?"); + // set our AuthOps + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + // AuthOps instance + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + // DocuDirCache instance + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + } + } + +}
--- a/servlet/src/digilib/servlet/PDFCache.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/PDFCache.java Fri Feb 05 20:58:38 2010 +0100 @@ -4,8 +4,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; @@ -58,8 +56,12 @@ public void init(ServletConfig config) throws ServletException{ super.init(config); - logger.info("initialized PDFCache v."+version); - + System.out.println("***** Digital Image Library Image PDF-Cache Servlet (version " + + version + ") *****"); +// say hello in the log file + logger.info("***** Digital Image Library Image PDF-Cache Servlet (version " + + version + ") *****"); + context = config.getServletContext(); dlConfig = (DigilibConfiguration) context.getAttribute("digilib.servlet.configuration"); @@ -117,23 +119,19 @@ int status = getStatus(docid); - - - if(status == STATUS_NONEXISTENT){ - createNewPdfDocument(pdfji, docid); - notifyUser(status, docid, request, response); - } - else if (status == STATUS_DONE){ - try { - sendFile(docid, downloadFilename(pdfji), response); - } catch (IOException e) { - e.printStackTrace(); - logger.error(e.getMessage()); - } - } - else { - notifyUser(status, docid, request, response); - } + if (status == STATUS_NONEXISTENT) { + createNewPdfDocument(pdfji, docid); + notifyUser(status, docid, request, response); + } else if (status == STATUS_DONE) { + try { + sendFile(docid, downloadFilename(pdfji), response); + } catch (IOException e) { + e.printStackTrace(); + logger.error(e.getMessage()); + } + } else { + notifyUser(status, docid, request, response); + } } /**
--- a/servlet/src/digilib/servlet/PDFJobInformation.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/PDFJobInformation.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,8 +1,8 @@ package digilib.servlet; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; @@ -80,7 +80,14 @@ String dh = image_info.getAsString("dh"); String pgs = getAsString("pgs"); - id = "fn=" + fn + "&dh=" + dh + "&pgs=" + pgs + ".pdf"; + id = "fn=" + fn + "&dh=" + dh + "&pgs=" + pgs + ".pdf"; + // make safe to use as filename by urlencoding + try { + id = URLEncoder.encode(id, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } else { id = null;
--- a/servlet/src/digilib/servlet/PDFMaker.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/PDFMaker.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,13 +1,9 @@ package digilib.servlet; import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/Parameter.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,275 @@ +/* Parameter -- General digilib parameter class. + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + * Created on 02.09.2003 by casties + * + */ +package digilib.servlet; + +import java.io.File; +import java.io.IOException; + +/** + * General digilib parameter class. + * + * @author casties + * + */ +public class Parameter { + /** real value */ + protected Object value = null; + + /** default value */ + protected Object defval = null; + + /** parameter name (e.g. in config file) */ + protected String name = null; + + /** parameter type */ + protected int type = 0; + + /** + * Default constructor. + * + */ + public Parameter() { + super(); + } + + /** + * Constructor with name, default, and value. + * + * @param value + * @param defval + */ + public Parameter(String name, Object defval, Object value) { + this.name = name; + this.value = value; + this.defval = defval; + } + + /** + * Constructor with name, default, value, and type. + * + * @param value + * @param defval + */ + public Parameter(String name, Object defval, Object value, int type) { + this.name = name; + this.value = value; + this.defval = defval; + this.type = type; + } + + /** + * Is the value valid. + * + * @return + */ + public boolean hasValue() { + return (value != null); + } + + /** + * Try to set the value from a String. + * + * Tries to convert the String to the same type as the default value. Sets + * the value anyway if the default is null. Returns if the value could be + * set. + * + * @param val + * @return + */ + public boolean setValueFromString(String val) { + // no default matches all + if (defval == null) { + this.value = val; + return true; + } + Class<? extends Object> c = defval.getClass(); + // take String as is + if (c == String.class) { + this.value = val; + return true; + } + // set File + if (c == File.class) { + this.value = new File(val); + return true; + } + // set Boolean if string == "true" + if (c == Boolean.class) { + this.value = new Boolean(val.compareToIgnoreCase("true") == 0); + return true; + } + try { + // set Integer + if (c == Integer.class) { + this.value = new Integer(Integer.parseInt(val)); + return true; + } + // set Float + if (c == Float.class) { + this.value = new Float(Float.parseFloat(val)); + return true; + } + } catch (NumberFormatException e) { + } + // then it's unknown + return false; + } + + /** + * Get the default as Object. + * + * @return + */ + public Object getDefault() { + return defval; + } + + /** + * Set the default. + * + * @param defval + */ + public void setDefault(Object defval) { + this.defval = defval; + } + + /** + * Get the value as Object. + * + * Returns the default if the value is not set. + * + * @return + */ + public Object getValue() { + return (value != null) ? value : defval; + } + + public int getAsInt() { + Integer i = (Integer) getValue(); + return (i != null) ? i.intValue() : 0; + } + + public float getAsFloat() { + Float f = (Float) getValue(); + return (f != null) ? f.floatValue() : 0f; + } + + public String getAsString() { + Object s = getValue(); + if (s == null) { + return ""; + } + if (s.getClass() == File.class) { + try { + return ((File) s).getCanonicalPath(); + } catch (IOException e) { + return "ERR: " + s.toString(); + } + } + return s.toString(); + } + + public boolean getAsBoolean() { + Boolean b = (Boolean) getValue(); + return (b != null) ? b.booleanValue() : false; + } + + public String[] parseAsArray(String separator) { + String s = getAsString(); + String[] sa = s.split(separator); + return sa; + } + + public float[] parseAsFloatArray(String separator) { + String s = getAsString(); + String[] sa = s.split(separator); + float[] fa = null; + try { + int n = sa.length; + fa = new float[n]; + for (int i = 0; i < n; i++) { + float f = Float.parseFloat(sa[i]); + fa[i] = f; + } + } catch (Exception e) { + } + + return fa; + } + + /** + * Set the value. + * + * @param value + */ + public void setValue(Object value) { + this.value = value; + } + + /** + * Set the value. + * + * @param value + */ + public void setValue(int value) { + this.value = new Integer(value); + } + + /** + * Set the value. + * + * @param value + */ + public void setValue(float value) { + this.value = new Float(value); + } + + /** + * @return + */ + public String getName() { + return name; + } + + /** + * @param name + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return + */ + public int getType() { + return type; + } + + /** + * @param type + */ + public void setType(int type) { + this.type = type; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/ParameterMap.java Fri Feb 05 20:58:38 2010 +0100 @@ -0,0 +1,251 @@ +/* ParameterMap.java -- HashMap of Parameters. + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * Created on 02.09.2003 by casties + * + */ +package digilib.servlet; + +import java.util.HashMap; + +/** HashMap of digilib.servlet.Parameter's. + * + * Keys are Strings. Values are Parameters. + * + * @author casties + * + */ +public class ParameterMap extends HashMap<String, Parameter> { + + private static final long serialVersionUID = 1530820988748391313L; + + /** Default constructor. + * + */ + public ParameterMap() { + super(); + } + + /** Construcotr with initial size. + * @param arg0 + */ + public ParameterMap(int arg0) { + super(arg0); + } + + /** Get the Parameter with the corresponding key. + * + * Returns null if no element is associated with key. + * + * @param key + * @return + */ + public Parameter get(String key) { + return super.get(key); + } + + /** Get the Parameter with the corresponding key. + * + * Returns null if no element is associated with key. + * + * @param key + * @return + */ + public Object getValue(String key) { + Parameter p = super.get(key); + return (p != null) ? p.getValue() : null; + } + + /** Get the Parameter with the corresponding key. + * + * Returns null if no element is associated with key. + * + * @param key + * @return + */ + public String getAsString(String key) { + Parameter p = super.get(key); + return (p != null) ? p.getAsString() : null; + } + + /** Get the Parameter with the corresponding key. + * + * Returns null if no element is associated with key. + * + * @param key + * @return + */ + public int getAsInt(String key) { + Parameter p = super.get(key); + return (p != null) ? p.getAsInt() : 0; + } + + /** Get the Parameter with the corresponding key. + * + * Returns null if no element is associated with key. + * + * @param key + * @return + */ + public float getAsFloat(String key) { + Parameter p = super.get(key); + return (p != null) ? p.getAsFloat() : 0f; + } + + /** Get the Parameter with the corresponding key. + * + * Returns null if no element is associated with key. + * + * @param key + * @return + */ + public boolean getAsBoolean(String key) { + Parameter p = super.get(key); + return (p != null) ? p.getAsBoolean() : false; + } + + /** Returns if the Parameter's value has been set. + * + * @param key + * @return + */ + public boolean hasValue(String key) { + Parameter p = super.get(key); + return (p != null) ? p.hasValue() : false; + } + + /** Add the Parameter to the map with a certain key. + * + * Returns the value that was previously associated with key. + * + * @param key + * @param val + * @return + */ + public Parameter put(String key, Parameter val) { + return super.put(key, val); + } + + /** Add the Parameter val to the map, using val's name. + * + * Returns the value that was previously associated with val's name. + * + * @param val + * @return + */ + public Parameter put(Parameter val) { + return super.put(val.getName(), val); + } + + /** Add a new Parameter with name, default and value. + * + * Returns the key that was previously associated with name. + * + * @param name + * @param def + * @param val + * @return + */ + public Parameter newParameter(String name, Object def, Object val) { + Parameter p = new Parameter(name, def, val); + return super.put(name, p); + } + + /** Add a new Parameter with name, default, value and type. + * + * Returns the key that was previously associated with name. + * + * @param name + * @param def + * @param val + * @param type + * @return + */ + public Parameter newParameter(String name, Object def, Object val, int type) { + Parameter p = new Parameter(name, def, val, type); + return super.put(name, p); + } + + /** Set the value of an existing parameter. + * + * Sets the value and returns true if the parameter exists. + * + * @param key + * @param val + * @return + */ + public boolean setValue(String key, Object val) { + Parameter p = get(key); + if (p != null) { + p.setValue(val); + return true; + } + return false; + } + + /** Set the value of an existing parameter. + * + * Sets the value and returns true if the parameter exists. + * + * @param key + * @param val + * @return + */ + public boolean setValue(String key, int val) { + Parameter p = get(key); + if (p != null) { + p.setValue(val); + return true; + } + return false; + } + + /** Set the value of an existing parameter. + * + * Sets the value and returns true if the parameter exists. + * + * @param key + * @param val + * @return + */ + public boolean setValue(String key, float val) { + Parameter p = get(key); + if (p != null) { + p.setValue(val); + return true; + } + return false; + } + + /** Set the value of an existing parameter. + * + * Sets the value and returns true if the parameter exists. + * + * @param key + * @param val + * @return + */ + public boolean setValueFromString(String key, String val) { + Parameter p = get(key); + if (p != null) { + p.setValueFromString(val); + return true; + } + return false; + } +}
--- a/servlet/src/digilib/servlet/RequestHandler.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/RequestHandler.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,7 +1,5 @@ package digilib.servlet; -import java.io.InputStream; - import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet;
--- a/servlet/src/digilib/servlet/Scaler.java Thu Jan 14 14:30:30 2010 +0100 +++ b/servlet/src/digilib/servlet/Scaler.java Fri Feb 05 20:58:38 2010 +0100 @@ -1,11 +1,7 @@ package digilib.servlet; -import java.awt.Image; -import java.awt.geom.Rectangle2D; -import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.List; @@ -15,8 +11,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.log4j.Logger; - import digilib.auth.AuthOpException; import digilib.auth.AuthOps; import digilib.image.ImageOpException; @@ -26,7 +20,6 @@ import digilib.io.FileOpException; import digilib.io.FileOps; import digilib.io.ImageFile; -import digilib.io.ImageFileset; // TODO digilibError is not used anymore and may need to get reintegrated @@ -248,7 +241,7 @@ /* check permissions */ if (useAuthorization) { // get a list of required roles (empty if no restrictions) - List rolesRequired; + List<String> rolesRequired; try { rolesRequired = authOp.rolesForPath(jobdeclaration.getFilePath(), request); if (rolesRequired != null) {