net.sf.ginp.browser.FolderManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.ginp.browser.FolderManagerImpl.java

Source

/*
 *  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);
            }
        }
    }
}