Rev 1272 | 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.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.TimestampRecipient;
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 (?)
private static final Logger logger = Logger.getLogger(Logic.class);
protected ConfigRoot config;
protected Map branches = new Hashtable(); // <String, Branch>
protected Digester configDigester = createConfigDigester();
protected Digester metaDigester = createMetaDigester();
protected Logic()
{
}
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() + "]");
// calc page number in index
index.setPage(pos / branch.getColumns() / branch.getRows());
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 boolean writeDir(String uri, String path, long ifModifiedSince,
OutputStream out, TimestampRecipient timestampRecipient)
throws IOException, LogicException
{
Branch branch = getBranch(uri);
File file = new File(branch.getImagesRoot(), path);
securePath(branch.getImagesRoot(), file);
return branch.getThumbnailer().writeDir(file, ifModifiedSince, out, timestampRecipient);
}
public boolean writeSmall(String uri, String path, long ifModifiedSince,
OutputStream out, TimestampRecipient timestampRecipient)
throws IOException, LogicException
{
Branch branch = getBranch(uri);
File file = new File(branch.getImagesRoot(), path);
securePath(branch.getImagesRoot(), file);
return branch.getThumbnailer().writeSmall(file, ifModifiedSince, out, timestampRecipient);
}
public boolean writeMedium(String uri, String path, long ifModifiedSince,
OutputStream out, TimestampRecipient timestampRecipient)
throws IOException, LogicException
{
Branch branch = getBranch(uri);
File file = new File(branch.getImagesRoot(), path);
securePath(branch.getImagesRoot(), file);
return branch.getThumbnailer().writeMedium(file, ifModifiedSince, out, timestampRecipient);
}
public boolean writeOrigin(String uri, String path, long ifModifiedSince,
OutputStream out, TimestampRecipient timestampRecipient)
throws IOException, LogicException
{
Branch branch = getBranch(uri);
FileInputStream in = null;
File file = new File(branch.getImagesRoot(), path);
securePath(branch.getImagesRoot(), file);
if(ifModifiedSince >= 0 && ifModifiedSince <= file.lastModified()) return false;
if(timestampRecipient != null) {
timestampRecipient.setTimestamp(file.lastModified());
}
try {
in = new FileInputStream(file);
FileUtils.copyStreams(in, out);
}
finally {
if(in != null) in.close();
}
return true;
}
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() + "]");
}
}