Subversion Repositories general

Rev

Rev 1255 | Rev 1267 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package ak.photoalbum.logic;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Hashtable;
import java.util.Iterator;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.FileNotFoundException;
import java.net.URLEncoder;

import org.xml.sax.SAXException;
import org.apache.commons.digester.Digester;
import org.apache.log4j.Logger;

import ak.photoalbum.util.FileUtils;
import ak.photoalbum.util.ResourceFactory;
import ak.photoalbum.config.ConfigRoot;
import ak.photoalbum.config.ConfigBranch;
import ak.photoalbum.config.ConfigDirThumbnail;

public class Logic
{
  protected static final String URL_ENCODING    = "UTF-8";
  protected static final String META_FILE_NAME  = "meta.xml"; // FIXME make configurable (?)

  protected Logger       logger;
  protected ConfigRoot   config;
  protected Map          branches           = new Hashtable(); // <String, Branch>
  protected Digester     configDigester     = createConfigDigester();
  protected Digester     metaDigester       = createMetaDigester();

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

  protected Digester createConfigDigester()
  {
    Digester digester = new Digester();
    digester.setValidating(false);

    digester.addObjectCreate(      "photos",                                ConfigRoot.class);
    digester.addBeanPropertySetter("photos/default-branch",                 "defaultBranch");
    
    digester.addObjectCreate(      "photos/branch",                         ConfigBranch.class);
    digester.addBeanPropertySetter("photos/branch/uri");
    digester.addBeanPropertySetter("photos/branch/images-root",             "imagesRoot");
    digester.addBeanPropertySetter("photos/branch/cache-dir",               "cacheDir");
    digester.addBeanPropertySetter("photos/branch/thumbnail-format",        "thumbnailFormat");
    digester.addBeanPropertySetter("photos/branch/columns");
    digester.addBeanPropertySetter("photos/branch/rows");
    digester.addCallMethod(        "photos/branch/images-mask",             "addImagesMask",
      0, new Class[] { String.class });
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/template",  "dirTemplate");

    digester.addObjectCreate(      "photos/branch/dir-thumbnail/thumbnail", ConfigDirThumbnail.class);
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/thumbnail/left");
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/thumbnail/top");
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/thumbnail/width");
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/thumbnail/height");
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/thumbnail/align");
    digester.addBeanPropertySetter("photos/branch/dir-thumbnail/thumbnail/valign");
    digester.addSetNext(           "photos/branch/dir-thumbnail/thumbnail", "addDirThumbnail");

    digester.addSetNext(           "photos/branch",                         "addBranch");

    return digester;
  }

  protected Digester createMetaDigester()
  {
    Digester digester = new Digester();
    digester.setValidating(false);

    digester.addObjectCreate("meta", MetaInfo.class);
    
    digester.addObjectCreate("meta/item", MetaInfoItem.class);
    digester.addSetProperties("meta/item", "id", "id");
    digester.addBeanPropertySetter("meta/item/title", "title");
    digester.addBeanPropertySetter("meta/item/subtitle", "subtitle");
    digester.addSetProperties("meta/item/subtitle", "mime", "subtitleMime");
    digester.addBeanPropertySetter("meta/item/comment", "comment");
    digester.addSetProperties("meta/item/comment", "mime", "commentMime");
    digester.addSetNext("meta/item", "addItem");

    return digester;
  }

  public void init(ResourceFactory resourceFactory, String configPath)
    throws IOException, SAXException, LogicException
  {
    logger.info("starting");
    config = (ConfigRoot)configDigester.parse(resourceFactory.getAsStream(configPath));

    for(Iterator i = config.getBranches().iterator(); i.hasNext(); ) {
      Branch branch = new Branch(resourceFactory, (ConfigBranch)i.next());
      branches.put(branch.getUri(), branch);
    }

    logger.info("started");
  }

  public Branch getBranch(String uri)
    throws LogicException
  {
    if(uri == null || uri.equals("")) {
      uri = config.getDefaultBranch();
      if(uri == null)
        throw new LogicException("No default branch configured");
    }

    Branch branch = (Branch)branches.get(uri);

    if(branch == null) 
      throw new LogicException("Branch not found");

    return branch;
  }

  public void buildCache(String uri)
    throws IOException, LogicException
  {
    getBranch(uri).getThumbnailer().buildCache();
  }

  public void rebuildCache(String uri)
    throws IOException, LogicException
  {
    getBranch(uri).getThumbnailer().rebuildCache();
  }

  public void deleteCache(String uri)
    throws IOException, LogicException
  {
    getBranch(uri).getThumbnailer().deleteCache();
  }

  public void reloadCache(String uri)
    throws IOException, LogicException
  {
    getBranch(uri).getThumbnailer().reloadCache();
  }

  public void getEntry(String uri, String path, IndexEntry page,
      IndexEntry index, IndexEntry prev, IndexEntry current, IndexEntry next)
    throws IOException, SAXException, LogicException
  {
    Branch branch = getBranch(uri);
    File   file   = new File(branch.getImagesRoot(), path);

    securePath(branch.getImagesRoot(), file);

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

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

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

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

    branch.getMetaInfos().clear();     // FIXME make this more intelligent
    setEntryInfo(branch, page,    file, false);
    setEntryInfo(branch, current, file, true);
    setEntryInfo(branch, index,   dir,  true);
    if(pos > 0)                 setEntryInfo(branch, prev, children[pos-1], true);
    if(pos < children.length-1) setEntryInfo(branch, next, children[pos+1], true);
  }

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

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

      if(small)
        size = branch.getThumbnailer().getSmallSize(file);
      else
        size = branch.getThumbnailer().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]);

    MetaInfoItem meta = findMetaInfo(branch, branch.getImagesRoot(), file);
    if(meta != null) {
      if(meta.getTitle() != null) {
        entry.setTitle(meta.getTitle());
      }
      if(meta.getSubtitle() != null) {
        entry.setSubtitle(meta.getSubtitle());
        entry.setSubtitleMime(meta.getSubtitleMime());
      }
      if(meta.getComment() != null) {
        entry.setComment(meta.getComment());
        entry.setCommentMime(meta.getCommentMime());
      }
    }
  }

  public String getThumbnailMime(String uri)
    throws LogicException
  {
    return getBranch(uri).getThumbnailer().getMime();
  }

  public String getOriginMime(String uri, String path)
    throws IOException, LogicException
  {
    Branch branch = getBranch(uri);
    File   file   = new File(branch.getImagesRoot(), path);

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

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

  public void writeDir(String uri, String path, OutputStream out)
    throws IOException, LogicException
  {
    Branch branch = getBranch(uri);
    File   file   = new File(branch.getImagesRoot(), path);

    securePath(branch.getImagesRoot(), file);
    branch.getThumbnailer().writeDir(file, out);
  }

  public void writeSmall(String uri, String path, OutputStream out)
    throws IOException, LogicException
  {
    Branch branch = getBranch(uri);
    File   file   = new File(branch.getImagesRoot(), path);

    securePath(branch.getImagesRoot(), file);
    branch.getThumbnailer().writeSmall(file, out);
  }

  public void writeMedium(String uri, String path, OutputStream out)
    throws IOException, LogicException
  {
    Branch branch = getBranch(uri);
    File   file   = new File(branch.getImagesRoot(), path);

    securePath(branch.getImagesRoot(), file);
    branch.getThumbnailer().writeMedium(file, out);
  }

  public void writeOrigin(String uri, String path, OutputStream out)
    throws IOException, LogicException
  {
    Branch          branch = getBranch(uri);
    FileInputStream in     = null;
    File            file   = new File(branch.getImagesRoot(), path);

    securePath(branch.getImagesRoot(), file);

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

  protected MetaInfo getMetaInfo(Branch branch, File dir)
    throws IOException, SAXException
  {
    MetaInfo meta = (MetaInfo)branch.getMetaInfos().get(dir);
    if(meta != null) return meta;

    File metaFile = new File(dir, META_FILE_NAME);
    if(!metaFile.exists()) return null;

    meta = (MetaInfo)metaDigester.parse(new FileInputStream(metaFile));
    meta.setDir(dir);
    branch.getMetaInfos().put(dir, meta);

    return meta;
  }

  protected MetaInfoItem findMetaInfo(Branch branch, File rootDir, File file)
    throws IOException, SAXException
  {
    file    = file.getCanonicalFile();
    rootDir = rootDir.getCanonicalFile();

    File dir = file;
    if(!dir.isDirectory())
      dir = dir.getParentFile();

    MetaInfoItem metaItem = null;
    for(; metaItem == null; dir = dir.getParentFile()) {
      if(dir == null) break;

      MetaInfo meta = getMetaInfo(branch, dir);
      if(meta != null) {
        metaItem = meta.findItem(file);
      }
      if(rootDir.equals(dir)) break;
    }

    return metaItem;
  }

  public boolean listDirectory(String uri, String dirName, int page, List table, List pages)
    throws IOException, LogicException, SAXException
  {
    Branch branch = getBranch(uri);
    File   dir    = new File(branch.getImagesRoot(), dirName);

    securePath(branch.getImagesRoot(), dir);
    if(!dir.exists()) return false;

    File[] children  = dir.listFiles(branch.getImagesFilter());
    int    pos       = page * branch.getColumns() * branch.getRows();

    Arrays.sort(children, branch.getFileNameComparator());
    branch.getMetaInfos().clear();           // FIXME do this more intelligent (?)

    // the pages list
    pages.clear();
    for(int i = 0; i < (int)Math.ceil((double)children.length / branch.getColumns() / branch.getRows()); i++) {
      pages.add(new PageItem(i, i == page));
    }

    // the main table
    table.clear();
    while(pos < children.length && pos < (page+1) * branch.getColumns() * branch.getRows()) {
      List row    = new ArrayList();
      int  rowPos = 0;

      table.add(row);

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

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

        IndexEntry entry = new IndexEntry(children[pos],
          URLEncoder.encode(path, URL_ENCODING),
          title, children[pos].isDirectory(), size[0], size[1]);

        MetaInfoItem meta = findMetaInfo(branch, branch.getImagesRoot(), children[pos]);
        if(meta != null) {
          if(meta.getTitle() != null) {
            entry.setTitle(meta.getTitle());
          }
          if(meta.getSubtitle() != null) {
            entry.setSubtitle(meta.getSubtitle());
            entry.setSubtitleMime(meta.getSubtitleMime());
          }
          if(meta.getComment() != null) {
            entry.setComment(meta.getComment());
            entry.setCommentMime(meta.getCommentMime());
          }
        }

        row.add(entry);
        rowPos++;
        pos++;
      }

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

    return true;
  }

  protected String getPath(Branch branch, File file)
    throws IOException
  {
    String path     = file.getCanonicalPath();
    String rootPath = branch.getImagesRoot().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 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() + "]");
  }
}