Java tutorial
/* * ginp - Java Web Application for Viewing Photo Collections * Copyright (C) 2004 Douglas John Culnane <doug@culnane.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ package net.sf.ginp.browser; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageDecoder; import com.sun.image.codec.jpeg.JPEGImageEncoder; import net.sf.ginp.PicCollection; import net.sf.ginp.config.Configuration; import net.sf.ginp.util.StringTool; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; /** * @author Justin Sher */ public class FolderManagerImpl implements FolderManager { //Cache is for one minute private static long MAX_CACHE = 60000; private static MakeThumbs mth = null; private static Thread mthThread = null; HashMap folderCache = new HashMap(); HashMap picCache = new HashMap(); long folderCacheTimeout = -1; long picCacheTimeout = -1; /** * apache Commons Logger specific to this class. */ private static Log log = LogFactory.getLog(FolderManagerImpl.class); /** * Gets the foldersInDirectory attribute of the PicCollection object. * @param root the root directory * @param relPath Description of the Parameter * @return The foldersInDirectory value */ private String[] getFoldersInDirectory(final String root, final String relPath) { long duration = System.currentTimeMillis() - picCacheTimeout; if ((picCacheTimeout < 0) || (duration > MAX_CACHE)) { synchronized (folderCache) { //Clearing a cache while doing a get can //cause hashmap to hang. Hence all the synchronized blocks folderCacheTimeout = System.currentTimeMillis(); folderCache.clear(); } } synchronized (folderCache) { if (folderCache.get(root + relPath) != null) { return (String[]) folderCache.get(root + relPath); } } Vector dirs = new Vector(); File dir = new File(root + relPath); if (dir.isDirectory()) { String[] files = dir.list(); Arrays.sort(files); for (int i = 0; i < files.length; i++) { File file = new File(root + relPath + "/" + files[i]); if (file.isDirectory()) { if (!(files[i].startsWith("."))) { dirs.add(files[i]); } } } } String[] retFolders = new String[dirs.size()]; for (int i = 0; i < dirs.size(); i++) { retFolders[i] = relPath + (String) dirs.get(i); } Arrays.sort(retFolders); synchronized (folderCache) { folderCache.put(root + relPath, retFolders); } return retFolders; } /** * Gets the picturesInDirectory attribute of the PicCollection object. * *@param relPath Description of the Parameter *@return The picturesInDirectory value */ private String[] getPicturesInDirectory(final String root, final String relPath) { long duration = System.currentTimeMillis() - picCacheTimeout; if ((picCacheTimeout < 0) || (duration > MAX_CACHE)) { synchronized (picCache) { //Clearing a cache while doing a get can //cause hashmap to hang. Hence all the synchronized blocks picCacheTimeout = System.currentTimeMillis(); picCache.clear(); } } synchronized (picCache) { if (picCache.get(root + relPath) != null) { String[] cached = (String[]) picCache.get(root + relPath); return cached; } } Vector pics = new Vector(); try { File dir = new File(root + relPath); if (dir.isDirectory()) { String[] files = dir.list(); for (int i = 0; i < files.length; i++) { File file = new File(root + relPath + "/" + files[i]); if ((!file.isDirectory()) && ((files[i].toLowerCase()).endsWith(".jpg") || (files[i].toLowerCase()).endsWith(".jpeg"))) { pics.add(files[i]); if (log.isDebugEnabled()) { log.debug("Adding picture file: " + files[i]); } } } // Add Featured Pics File fl = new File(root + relPath + "ginpfolder.xml"); if (fl.exists()) { FileReader fr = new FileReader(fl); LineNumberReader lr = new LineNumberReader(fr); StringBuffer sb = new StringBuffer(); String temp; while ((temp = lr.readLine()) != null) { sb.append(temp + "\n"); } String featuredpicsXML = StringTool.getXMLTagContent("featuredpics", sb.toString()); String[] picsXML = StringTool.splitToArray(featuredpicsXML, "<pic>"); for (int i = 0; i < picsXML.length; i++) { if (picsXML[i].indexOf("</pic>") != -1) { temp = picsXML[i].substring(0, picsXML[i].indexOf("</pic>")); fl = new File(root + relPath + temp); if (fl.exists()) { pics.add(temp); } } } } } } catch (IOException ex) { log.error(ex); } String[] retPictures = new String[pics.size()]; for (int i = 0; i < pics.size(); i++) { retPictures[i] = (String) pics.get(i); } String[] sorted = sortPictures(retPictures); synchronized (picCache) { if (picCache.get(root + relPath) == null) { picCache.put(root + relPath, sorted); } else { return (String[]) picCache.get(root + relPath); } } // Only One Thread Per Directory Should Ever Get Here // (Unless it takes longer than 2 minutes to make) synchronized (MakeThumbs.class) { if ((mth == null) || ((mthThread != null) && !mthThread.isAlive())) { mth = new MakeThumbs(); mthThread = new Thread(mth); mthThread.setDaemon(true); mthThread.setPriority(Thread.MIN_PRIORITY); mthThread.start(); } } mth.addToQueue(root + relPath, sorted); return sorted; } /** * Sort the arry of picture names. * *@param ary Array of unSorted Picture Names *@return Sorted array based on current Sort criteria. */ public final String[] sortPictures(final String[] ary) { //if (sortDESC) { //revrce sort //} Comparator comp = new ComparePictures(); Arrays.sort(ary, comp); return ary; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getFoldersLength( *net.sf.ginp.PicCollection) */ public final int getFoldersLength(final PicCollection collection) { return this.getFoldersInDirectory(collection.getRoot(), collection.getPath()).length; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getPicturesInDirectoryLength( *java.lang.String) */ public final int getPicturesInDirectoryLength(final PicCollection collection, final String selectedFolder) { return this.getPicturesInDirectory(collection, selectedFolder).length; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getFoldersInDirectory( *java.lang.String) */ public final String[] getFoldersInDirectory(final PicCollection collection, final String string) { return this.getFoldersInDirectory(collection.getRoot(), string); } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getPicturesInDirectory( *java.lang.String) */ public final String[] getPicturesInDirectory(final PicCollection collection, final String path) { return this.getPicturesInDirectory(collection.getRoot(), path); } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getPrevPictureName( *java.lang.String, net.sf.ginp.PicCollection) */ public final String getPrevPictureName(final String picName, final PicCollection collection) { String[] pictures2 = this.getPictures(collection); if (pictures2[0].equals(picName)) { return null; } for (int x = 1; x < pictures2.length; x++) { String pic = pictures2[x]; if (pic.equals(picName)) { return pictures2[--x]; } } return null; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getNextPictureName( *java.lang.String, net.sf.ginp.PicCollection) */ public final String getNextPictureName(final String picName, final PicCollection collection) { String[] pictures2 = this.getPictures(collection); if (pictures2[pictures2.length - 1].equals(picName)) { return null; } for (int x = 0; x < (pictures2.length - 1); x++) { String pic = pictures2[x]; if (pic.equals(picName)) { return pictures2[++x]; } } return null; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getPicturesLength( *java.lang.String) */ public final int getPicturesLength(final PicCollection collection, final String path) { return this.getPicturesInDirectory(collection, path).length; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getPictures( *net.sf.ginp.PicCollection) */ public final String[] getPictures(final PicCollection collection) { return this.getPicturesInDirectory(collection.getRoot(), collection.getPath()); } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getPicturesLength( *net.sf.ginp.PicCollection) */ public final int getPicturesLength(final PicCollection collection) { return this.getPicturesInDirectory(collection.getRoot(), collection.getPath()).length; } /* (non-Javadoc). * @see net.sf.ginp.browser.FolderManager#getFolder( *net.sf.ginp.PicCollection, int) */ public final String getFolder(final PicCollection collection, final int count) { return this.getFoldersInDirectory(collection.getRoot(), collection.getPath())[count]; } /** * Description of the Class. * *@author doc *@version $Revision$ */ class ComparePictures implements Comparator { /** * Constructor for the ComparePictures object. */ ComparePictures() { } /** * Description of the Method. * *@param o1 Description of the Parameter *@param o2 Description of the Parameter *@return Description of the Return Value */ public int compare(Object o1, Object o2) { if (o1.toString().indexOf("/") != -1) { o1 = o1.toString().substring(o1.toString().lastIndexOf("/") + 1); } if (o2.toString().indexOf("/") != -1) { o2 = o2.toString().substring(o2.toString().lastIndexOf("/") + 1); } return o1.toString().compareTo(o2.toString()); } /** * Description of the Method. * *@param obj Description of the Parameter *@return Description of the Return Value */ public boolean equals(Object obj) { return true; } } } /** * A class that makes Thumbnail images. This is designed to be used as a * background thread. * *@author $Author$ *@version $Revision$ */ class MakeThumbs implements Runnable { ArrayList thumbQueue = new ArrayList(); /** * apache Commons Logger specific to this class. */ private Log log = LogFactory.getLog(MakeThumbs.class); /** * Add a request to make thumbs for a particular directory. * @param path the path of the directory * @param pics the pictures in the directory */ public void addToQueue(final String path, final String[] pics) { synchronized (thumbQueue) { Iterator iter = thumbQueue.iterator(); while (iter.hasNext()) { Object[] queue = (Object[]) iter.next(); if (queue[0].equals(path)) { //Already have one in the queue return; } } thumbQueue.add(new Object[] { path, pics }); } } /** * Main processing method for the MakeThumbs object. */ public void run() { String url = null; String[] pics = null; int[] thumbSizes = { Configuration.getThumbSize(), Configuration.getFilmStripThumbSize() }; while (true) { boolean makeQueue = false; synchronized (thumbQueue) { if (thumbQueue.size() > 0) { Object[] queue = (Object[]) thumbQueue.remove(0); url = (String) queue[0]; pics = (String[]) queue[1]; makeQueue = true; } } if (!makeQueue) { try { Thread.sleep(1000); continue; } catch (InterruptedException e1) { // TODO Auto-generated catch block log.error(e1); //.printStackTrace(); } } try { File folder = new File(url); if (folder.exists()) { File ginpFolder = new File(url + "/.ginp"); if (!(ginpFolder.exists())) { ginpFolder.mkdir(); } // For each picture make a thumb; for (int i = 0; i < pics.length; i++) { makeThumbImage(url, pics[i], thumbSizes); } } } catch (Exception e) { log.error(e); } } } void makeThumbImage(final String url, final String fileName, final int[] sizes) { if (log.isDebugEnabled()) { log.debug("makeThumbImage: url=" + url + " fileName=" + fileName + " int[] sizes"); } for (int i = 0; i < sizes.length; i++) { try { String thumbFile = getThumbFileName(url, sizes[i], fileName); File ginpPic = new File(thumbFile); File origPic = new File(url + "/" + fileName); boolean makeThumb = false; if (ginpPic.exists()) { // ReThumb if the original changed after the thumb. if (origPic.lastModified() > ginpPic.lastModified()) { makeThumb = true; } } else { makeThumb = true; } if (makeThumb) { if (i != 0) { File bigThumb = new File(getThumbFileName(url, sizes[i - 1], fileName)); if (bigThumb.exists()) { origPic = bigThumb; } } makeThumbImage(origPic, thumbFile, sizes[i]); } } catch (Exception e) { log.error(e); } } } String getThumbFileName(final String url, final int size, final String fileName) { String retFileName = url + "/.ginp/" + size + "-" + fileName; // If this is a featured picture fix the path. if (fileName.indexOf("/") != -1) { retFileName = url + fileName.substring(0, fileName.lastIndexOf("/")) + "/.ginp/" + size + "-" + fileName.substring(fileName.lastIndexOf("/") + 1); } return retFileName; } void makeThumbImage(final File origPicture, final String thumbFileName, final int maxThumbSize) { if (log.isDebugEnabled()) { log.debug("makeThumbImage: origFileName=" + origPicture.getAbsolutePath() + " thumbFileName=" + thumbFileName + " maxThumbSize=" + maxThumbSize); } // Only jpegs supported. if ((origPicture.getName().toLowerCase()).endsWith(".jpg") || (origPicture.getName().toLowerCase()).endsWith(".jpeg")) { try { // thumb it. JPEGImageDecoder dc = JPEGCodec.createJPEGDecoder((new FileInputStream(origPicture))); BufferedImage origImage = dc.decodeAsBufferedImage(); int origHeight = origImage.getHeight(null); int origWidth = origImage.getWidth(null); int scaledW = 0; int scaledH = 0; double scale = 1.0; if (origHeight < origWidth) { scale = (double) maxThumbSize / (double) origWidth; } else { scale = (double) maxThumbSize / (double) origHeight; } scaledW = (int) (scale * origWidth); scaledH = (int) (scale * origHeight); //AffineTransform at = new AffineTransform(); AffineTransform tx; AffineTransformOp af; JPEGImageEncoder encoder; BufferedImage outImage; outImage = new BufferedImage(scaledW, scaledH, BufferedImage.TYPE_INT_RGB); tx = new AffineTransform(); tx.scale(scale, scale); af = new AffineTransformOp(tx, null); af.filter(origImage, outImage); File ginpFolder = new File( thumbFileName.substring(0, thumbFileName.lastIndexOf("/.ginp")) + "/.ginp"); if (!(ginpFolder.exists())) { ginpFolder.mkdir(); } encoder = JPEGCodec.createJPEGEncoder(new FileOutputStream(thumbFileName)); encoder.encode(outImage); } catch (Exception e) { log.error("Error Makeing Thumb Image " + thumbFileName, e); } } } }