Subversion Repositories general

Rev

Blame | Last modification | View Log | RSS feed

package ak.photoalbum.webapp;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.io.FileNotFoundException;
import java.net.URLEncoder;
import org.apache.log4j.Logger;
import ak.photoalbum.images.Thumbnailer;
import ak.photoalbum.images.ThumbnailPosition;
import ak.photoalbum.util.FileUtils;
import ak.photoalbum.util.FileNameComparator;
import ak.photoalbum.util.ImagesFilter;

public class Logic
{
  protected static final int    DEFAULT_COLUMNS = 4;
  protected static final String URL_ENCODING    = "UTF-8";

  protected Logger       logger;
  protected Thumbnailer  thumbnailer;
  protected File         imagesRoot;
  protected int          columns            = DEFAULT_COLUMNS;
  protected ImagesFilter imagesFilter;
  protected Comparator   fileNameComparator = new FileNameComparator(true);

  protected Logic()
  {
    this.logger = Logger.getLogger(this.getClass());
  }

  public void init(String imagesRoot, String imagesMask,
    String cacheDir, String thumbnailFormat,
    Integer smallWidth, Integer smallHeight,
    Integer mediumWidth, Integer mediumHeight, Integer columns,
    String  dirTemplate, String  dirThumbnailPositions)
  {
    this.imagesRoot   = new File(imagesRoot);
    this.imagesFilter = new ImagesFilter(imagesMask);
    if(columns != null) this.columns = columns.intValue();

    this.thumbnailer = new Thumbnailer();
    this.thumbnailer.setImagesRoot(this.imagesRoot);
    this.thumbnailer.setCacheDir(new File(cacheDir));

    if(thumbnailFormat != null)
      this.thumbnailer.setFormat(thumbnailFormat);
    if(smallWidth != null)
      this.thumbnailer.setSmallWidth(smallWidth.intValue());
    if(smallHeight != null)
      this.thumbnailer.setSmallHeight(smallHeight.intValue());
    if(mediumWidth != null)
      this.thumbnailer.setMediumWidth(mediumWidth.intValue());
    if(mediumHeight != null)
      this.thumbnailer.setMediumHeight(mediumHeight.intValue());

    thumbnailer.setResizer(new ak.photoalbum.images.jiu.JiuResizer());
    thumbnailer.setImagesFilter(this.imagesFilter);
    thumbnailer.setDirTemplate(new File(dirTemplate));
    thumbnailer.setDirThumbnailPositions(
      parseThumbnailPositions(dirThumbnailPositions));

    try {
      thumbnailer.startup();
    }
    catch(Exception ex) {
      logger.error("init thumbnailer", ex);
    }

    logger.info("started");
  }

  public void buildCache()
    throws IOException
  {
    thumbnailer.buildCache();
  }

  public void getEntry(String path, IndexEntry page,
      IndexEntry index, IndexEntry prev, IndexEntry current, IndexEntry next)
    throws IOException, LogicException
  {
    File file = new File(imagesRoot, path);

    securePath(imagesRoot, file);

    if(!file.exists())
      throw new FileNotFoundException(
        "[" + file.getCanonicalPath() + "] not found");

    File   dir      = file.getParentFile();
    File[] children = dir.listFiles(imagesFilter);
    int    pos;

    Arrays.sort(children, fileNameComparator);
    pos = Arrays.binarySearch(children, file, fileNameComparator);

    if(pos < 0)
      throw new FileNotFoundException("[" + file.getCanonicalPath()
        + "] not found in [" + dir.getCanonicalPath() + "]");

    setEntryInfo(page,    file, false);
    setEntryInfo(current, file, true);
    setEntryInfo(index,   dir,  true);
    if(pos > 0)                 setEntryInfo(prev,  children[pos-1], true);
    if(pos < children.length-1) setEntryInfo(next,  children[pos+1], true);
  }

  protected void setEntryInfo(IndexEntry entry, File file, boolean small)
    throws IOException
  {
    String title = file.getName();
    int[]  size;
    String path  = getPath(file);

    if(file.isDirectory()) {
      size = thumbnailer.getDirSize(file);
    }
    else {
       title = FileUtils.extractFileName(title);

      if(small)
        size = thumbnailer.getSmallSize(file);
       else
        size = thumbnailer.getMediumSize(file);
    }

    entry.setFile(file);
    entry.setPath(path == null ? null : URLEncoder.encode(path, URL_ENCODING));
    entry.setTitle(title);
    entry.setIsDir(file.isDirectory());
    entry.setWidth(size[0]);
    entry.setHeight(size[1]);
  }

  public String getThumbnailMime()
  {
    return thumbnailer.getMime();
  }

  public String getOriginMime(String path)
    throws IOException, LogicException
  {
    File file = new File(imagesRoot, path);

    if(!file.exists()) return null;
    securePath(imagesRoot, file);

    return FileUtils.getMime(FileUtils.extractFileExt(path));
  }

  public void writeDir(String path, OutputStream out)
    throws IOException, LogicException
  {
    File file = new File(imagesRoot, path);

    securePath(imagesRoot, file);
    thumbnailer.writeDir(file, out);
  }

  public void writeSmall(String path, OutputStream out)
    throws IOException, LogicException
  {
    File file = new File(imagesRoot, path);

    securePath(imagesRoot, file);
    thumbnailer.writeSmall(file, out);
  }

  public void writeMedium(String path, OutputStream out)
    throws IOException, LogicException
  {
    File file = new File(imagesRoot, path);

    securePath(imagesRoot, file);
    thumbnailer.writeMedium(file, out);
  }

  public void writeOrigin(String path, OutputStream out)
    throws IOException, LogicException
  {
    FileInputStream in   = null;
    File            file = new File(imagesRoot, path);

    securePath(imagesRoot, file);

    try {
      in  = new FileInputStream(file);
      FileUtils.copyStreams(in, out);
    }
    finally {
      if(in != null) in.close();
    }
  }

  public List listDirectory(String dirName)
    throws UnsupportedEncodingException, IOException, LogicException
  {
    File dir = new File(imagesRoot, dirName);

    securePath(imagesRoot, dir);
    if(!dir.exists()) return null;

    File[] children = dir.listFiles(imagesFilter);
    List   rows     = new ArrayList();
    int    pos      = 0;

    Arrays.sort(children, fileNameComparator);

    while(pos < children.length) {
      List row    = new ArrayList();
      int  rowPos = 0;

      rows.add(row);

      while(rowPos < columns && pos < children.length) {
        String path  = getPath(children[pos]);
        String title = children[pos].getName();
        int[]  size;

        if(children[pos].isDirectory()) {
          size  = thumbnailer.getDirSize(children[pos]);
        }
        else {
          size  = thumbnailer.getSmallSize(children[pos]);
          title = FileUtils.extractFileName(title);
        }

        row.add(new IndexEntry(children[pos],
          URLEncoder.encode(path, URL_ENCODING),
          title, children[pos].isDirectory(), size[0], size[1]));
        rowPos++;
        pos++;
      }

      while(rowPos < columns) {
        row.add(null);
        rowPos++;
      }
    }

    return rows;
  }

  protected String getPath(File file)
    throws IOException
  {
    String path     = file.getCanonicalPath();
    String rootPath = imagesRoot.getCanonicalPath();

    if(path.equals(rootPath)) return "";
    if(!rootPath.endsWith(File.separator)) rootPath += File.separator;

    if(!path.startsWith(rootPath))
      return null;

    return path.substring(rootPath.length());
  }

  protected ThumbnailPosition[] parseThumbnailPositions(String str)
  {
    List            list      = new ArrayList();
    StringTokenizer tokenizer = new StringTokenizer(str, ";");

    while(tokenizer.hasMoreTokens()) {
      StringTokenizer tokenParser
        = new StringTokenizer(tokenizer.nextToken(), ",");
      int    x            = Integer.parseInt(tokenParser.nextToken().trim());
      int    y            = Integer.parseInt(tokenParser.nextToken().trim());
      int    width        = Integer.parseInt(tokenParser.nextToken().trim());
      int    height       = Integer.parseInt(tokenParser.nextToken().trim());
      String horAlignStr  = tokenParser.nextToken().trim().toLowerCase();
      String vertAlignStr = tokenParser.nextToken().trim().toLowerCase();
      int    horAlign;
      int    vertAlign;

      if("l".equals(horAlignStr))
        horAlign = ThumbnailPosition.ALIGN_HOR_LEFT;
      else if("r".equals(horAlignStr))
        horAlign = ThumbnailPosition.ALIGN_HOR_RIGHT;
      else if("c".equals(horAlignStr))
        horAlign = ThumbnailPosition.ALIGN_HOR_CENTER;
      else
        throw new RuntimeException(
          "Cannot parse " + horAlignStr + " as horizontal alignment");

      if("t".equals(vertAlignStr))
        vertAlign = ThumbnailPosition.ALIGN_VERT_TOP;
      else if("b".equals(vertAlignStr))
        vertAlign = ThumbnailPosition.ALIGN_VERT_BOTTOM;
      else if("c".equals(vertAlignStr))
        vertAlign = ThumbnailPosition.ALIGN_VERT_CENTER;
      else
        throw new RuntimeException(
          "Cannot parse " + vertAlignStr + " as vertical alignment");

      list.add(new ThumbnailPosition(x, y, width, height, horAlign, vertAlign));
    }

    ThumbnailPosition[] res = new ThumbnailPosition[list.size()];

    for(int i = 0; i < res.length; i++)
      res[i] = (ThumbnailPosition)list.get(i);

    return res;
  }

  protected static final Logic instance = new Logic();

  public static Logic getLogic()
  {
    return instance;
  }

  /**
   * checks if given file is really under the parent directory
   */
  protected void securePath(File parentDir, File file)
    throws IOException, LogicException
  {
    if(parentDir == null || file == null) return;

    File partFile = file.getCanonicalFile();

    parentDir = parentDir.getCanonicalFile();
    while(partFile != null) {
      if(partFile.equals(parentDir)) return;
      partFile = partFile.getParentFile();
    }

    throw new LogicSecurityException(
      "[" + file.getCanonicalPath() + "] is outside of directory ["
      + parentDir.getCanonicalPath() + "]");
  }
}