Rev 1247 | Rev 1249 | Go to most recent revision | Blame | Compare with Previous | 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.util.Map;
import java.util.Hashtable;
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.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 = 5;
protected static final int DEFAULT_ROWS = 4;
protected static final String URL_ENCODING = "UTF-8";
protected static final String META_FILE_NAME = "meta.xml"; // FIXME make configurable (?)
protected Logger logger;
protected Thumbnailer thumbnailer;
protected ConfigRoot config;
protected File imagesRoot;
protected int columns = DEFAULT_COLUMNS;
protected int rows = DEFAULT_ROWS;
protected ImagesFilter imagesFilter;
protected Comparator fileNameComparator = new FileNameComparator(true);
protected Digester configDigester = createConfigDigester();
protected Digester metaDigester = createMetaDigester();
protected Map metaInfos = new Hashtable(); // <File, MetaInfo>
protected Logic()
{
this.logger = Logger.getLogger(this.getClass());
}
protected Digester createConfigDigester()
{
/*
4 <uri>children</uri>
5 <images-root>/home/data/photos</images-root>
6 <cache-dir>/home/data/photos/.cache</cache-dir>
7 <thumbnail-format>jpg</thumbnail-format>
8 <columns>6</columns>
9 <rows>5</rows>
10 <images-mask>*.jpeg</images-mask>
11 <images-mask>*.jpg</images-mask>
12 <dir-thumbnail>
13 <template>images/dir_template.gif</template>
14 <thumbnail>
15 <left>5</left>
16 <top>9</top>
17 <width>40</width>
18 <height>40</height>
19 <align>center</align>
20 <valign>center</valign>
*/
Digester digester = new Digester();
digester.setValidating(false);
digester.addObjectCreate("photos", ConfigRoot.class);
digester.addObjectCreate("photos/branch", ConfigBranch.class);
digester.addBeanPropertySetter("photos/branch/uri", "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", "columns");
digester.addBeanPropertySetter("photos/branch/rows", "rows");
// FIXME images-mask
digester.addSetNext("photos/branch", "addBranch");
/* 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("photos/branch", "addItem");
*/
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)
{
logger.info("starting");
config = (ConfigRoot)configDigester.parse(resourceFactory.getAsStream(configPath));
/*
this.imagesRoot = new File(imagesRoot);
this.imagesFilter = new ImagesFilter(imagesMask);
if(columns != null) this.columns = columns.intValue();
if(rows != null) this.rows = rows.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, SAXException, 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() + "]");
metaInfos.clear(); // FIXME make this more intelligent
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, SAXException
{
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]);
MetaInfoItem meta = findMetaInfo(imagesRoot, 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()
{
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();
}
}
protected MetaInfo getMetaInfo(File dir)
throws IOException, SAXException
{
MetaInfo meta = (MetaInfo)metaInfos.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);
metaInfos.put(dir, meta);
return meta;
}
protected MetaInfoItem findMetaInfo(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(dir);
if(meta != null) {
metaItem = meta.findItem(file);
}
if(rootDir.equals(dir)) break;
}
return metaItem;
}
public boolean listDirectory(String dirName, int page, List table, List pages)
throws IOException, LogicException, SAXException
{
File dir = new File(imagesRoot, dirName);
securePath(imagesRoot, dir);
if(!dir.exists()) return false;
File[] children = dir.listFiles(imagesFilter);
int pos = page * columns * rows;
Arrays.sort(children, fileNameComparator);
metaInfos.clear(); // FIXME do this more intelligent (?)
// the pages list
pages.clear();
for(int i = 0; i < (int)Math.ceil((double)children.length / columns / rows); i++) {
pages.add(new PageItem(i, i == page));
}
// the main table
table.clear();
while(pos < children.length && pos < (page+1) * columns * rows) {
List row = new ArrayList();
int rowPos = 0;
table.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);
}
IndexEntry entry = new IndexEntry(children[pos],
URLEncoder.encode(path, URL_ENCODING),
title, children[pos].isDirectory(), size[0], size[1]);
MetaInfoItem meta = findMetaInfo(imagesRoot, 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 < columns) {
row.add(null);
rowPos++;
}
}
return true;
}
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() + "]");
}
}