Subversion Repositories general

Rev

Rev 1247 | Rev 1249 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
936 dev 1
package ak.photoalbum.webapp;
2
 
3
import java.util.List;
4
import java.util.ArrayList;
5
import java.util.Arrays;
6
import java.util.Comparator;
7
import java.util.StringTokenizer;
1242 dev 8
import java.util.Map;
9
import java.util.Hashtable;
936 dev 10
import java.io.File;
11
import java.io.IOException;
12
import java.io.FileInputStream;
1242 dev 13
import java.io.BufferedReader;
14
import java.io.InputStreamReader;
936 dev 15
import java.io.OutputStream;
16
import java.io.FileNotFoundException;
17
import java.net.URLEncoder;
1242 dev 18
import org.xml.sax.SAXException;
19
import org.apache.commons.digester.Digester;
936 dev 20
import org.apache.log4j.Logger;
21
import ak.photoalbum.images.Thumbnailer;
22
import ak.photoalbum.images.ThumbnailPosition;
23
import ak.photoalbum.util.FileUtils;
24
import ak.photoalbum.util.FileNameComparator;
25
import ak.photoalbum.util.ImagesFilter;
26
 
27
public class Logic
28
{
1242 dev 29
  protected static final int    DEFAULT_COLUMNS = 5;
30
  protected static final int    DEFAULT_ROWS    = 4;
936 dev 31
  protected static final String URL_ENCODING    = "UTF-8";
1242 dev 32
  protected static final String META_FILE_NAME  = "meta.xml"; // FIXME make configurable (?)
936 dev 33
 
34
  protected Logger       logger;
35
  protected Thumbnailer  thumbnailer;
1247 dev 36
  protected ConfigRoot   config;
936 dev 37
  protected File         imagesRoot;
38
  protected int          columns            = DEFAULT_COLUMNS;
1242 dev 39
  protected int          rows               = DEFAULT_ROWS;
936 dev 40
  protected ImagesFilter imagesFilter;
41
  protected Comparator   fileNameComparator = new FileNameComparator(true);
1247 dev 42
  protected Digester     configDigester     = createConfigDigester();
1242 dev 43
  protected Digester     metaDigester       = createMetaDigester();
44
  protected Map          metaInfos          = new Hashtable(); // <File, MetaInfo>
936 dev 45
 
46
  protected Logic()
47
  {
48
    this.logger = Logger.getLogger(this.getClass());
49
  }
50
 
1247 dev 51
  protected Digester createConfigDigester()
936 dev 52
  {
1247 dev 53
    /*
54
4     <uri>children</uri>
55
5     <images-root>/home/data/photos</images-root>
56
6     <cache-dir>/home/data/photos/.cache</cache-dir>
57
7     <thumbnail-format>jpg</thumbnail-format>
58
8     <columns>6</columns>
59
9     <rows>5</rows>
60
10     <images-mask>*.jpeg</images-mask>
61
11     <images-mask>*.jpg</images-mask>
62
12     <dir-thumbnail>
63
13       <template>images/dir_template.gif</template>
64
14       <thumbnail>
65
15         <left>5</left>
66
16         <top>9</top>
67
17         <width>40</width>
68
18         <height>40</height>
69
19         <align>center</align>
70
20         <valign>center</valign>
71
*/
72
    Digester digester = new Digester();
73
    digester.setValidating(false);
74
 
75
    digester.addObjectCreate("photos", ConfigRoot.class);
76
 
77
    digester.addObjectCreate("photos/branch", ConfigBranch.class);
78
    digester.addBeanPropertySetter("photos/branch/uri",              "uri");
79
    digester.addBeanPropertySetter("photos/branch/images-root",      "imagesRoot");
80
    digester.addBeanPropertySetter("photos/branch/cache-dir",        "cacheDir");
81
    digester.addBeanPropertySetter("photos/branch/thumbnail-format", "thumbnailFormat");
82
    digester.addBeanPropertySetter("photos/branch/columns",          "columns");
83
    digester.addBeanPropertySetter("photos/branch/rows",             "rows");
84
 
85
// FIXME images-mask
86
 
87
    digester.addSetNext("photos/branch", "addBranch");
88
 /*   digester.addBeanPropertySetter("meta/item/subtitle", "subtitle");
89
    digester.addSetProperties("meta/item/subtitle", "mime", "subtitleMime");
90
    digester.addBeanPropertySetter("meta/item/comment", "comment");
91
    digester.addSetProperties("meta/item/comment", "mime", "commentMime");
92
    digester.addSetNext("photos/branch", "addItem");
93
*/
94
    return digester;
95
  }
96
 
97
  protected Digester createMetaDigester()
98
  {
99
    Digester digester = new Digester();
100
    digester.setValidating(false);
101
 
102
    digester.addObjectCreate("meta", MetaInfo.class);
103
 
104
    digester.addObjectCreate("meta/item", MetaInfoItem.class);
105
    digester.addSetProperties("meta/item", "id", "id");
106
    digester.addBeanPropertySetter("meta/item/title", "title");
107
    digester.addBeanPropertySetter("meta/item/subtitle", "subtitle");
108
    digester.addSetProperties("meta/item/subtitle", "mime", "subtitleMime");
109
    digester.addBeanPropertySetter("meta/item/comment", "comment");
110
    digester.addSetProperties("meta/item/comment", "mime", "commentMime");
111
    digester.addSetNext("meta/item", "addItem");
112
 
113
    return digester;
114
  }
115
 
116
  public void init(ResourceFactory resourceFactory, String configPath)
117
  {
118
    logger.info("starting");
119
    config = (ConfigRoot)configDigester.parse(resourceFactory.getAsStream(configPath));
120
/*
936 dev 121
    this.imagesRoot   = new File(imagesRoot);
122
    this.imagesFilter = new ImagesFilter(imagesMask);
123
    if(columns != null) this.columns = columns.intValue();
1242 dev 124
    if(rows    != null) this.rows    = rows.intValue();
936 dev 125
 
126
    this.thumbnailer = new Thumbnailer();
127
    this.thumbnailer.setImagesRoot(this.imagesRoot);
128
    this.thumbnailer.setCacheDir(new File(cacheDir));
129
 
130
    if(thumbnailFormat != null)
131
      this.thumbnailer.setFormat(thumbnailFormat);
132
    if(smallWidth != null)
133
      this.thumbnailer.setSmallWidth(smallWidth.intValue());
134
    if(smallHeight != null)
135
      this.thumbnailer.setSmallHeight(smallHeight.intValue());
136
    if(mediumWidth != null)
137
      this.thumbnailer.setMediumWidth(mediumWidth.intValue());
138
    if(mediumHeight != null)
139
      this.thumbnailer.setMediumHeight(mediumHeight.intValue());
140
 
141
    thumbnailer.setResizer(new ak.photoalbum.images.jiu.JiuResizer());
142
    thumbnailer.setImagesFilter(this.imagesFilter);
143
    thumbnailer.setDirTemplate(new File(dirTemplate));
144
    thumbnailer.setDirThumbnailPositions(
145
      parseThumbnailPositions(dirThumbnailPositions));
146
 
147
    try {
148
      thumbnailer.startup();
149
    }
150
    catch(Exception ex) {
151
      logger.error("init thumbnailer", ex);
152
    }
1247 dev 153
*/
936 dev 154
    logger.info("started");
155
  }
156
 
157
  public void buildCache()
158
    throws IOException
159
  {
160
    thumbnailer.buildCache();
161
  }
162
 
163
  public void getEntry(String path, IndexEntry page,
164
      IndexEntry index, IndexEntry prev, IndexEntry current, IndexEntry next)
1242 dev 165
    throws IOException, SAXException, LogicException
936 dev 166
  {
167
    File file = new File(imagesRoot, path);
168
 
169
    securePath(imagesRoot, file);
170
 
171
    if(!file.exists())
172
      throw new FileNotFoundException(
173
        "[" + file.getCanonicalPath() + "] not found");
174
 
175
    File   dir      = file.getParentFile();
176
    File[] children = dir.listFiles(imagesFilter);
177
    int    pos;
178
 
179
    Arrays.sort(children, fileNameComparator);
180
    pos = Arrays.binarySearch(children, file, fileNameComparator);
181
 
182
    if(pos < 0)
183
      throw new FileNotFoundException("[" + file.getCanonicalPath()
184
        + "] not found in [" + dir.getCanonicalPath() + "]");
185
 
1242 dev 186
    metaInfos.clear();     // FIXME make this more intelligent
936 dev 187
    setEntryInfo(page,    file, false);
188
    setEntryInfo(current, file, true);
189
    setEntryInfo(index,   dir,  true);
1242 dev 190
    if(pos > 0)                 setEntryInfo(prev, children[pos-1], true);
191
    if(pos < children.length-1) setEntryInfo(next, children[pos+1], true);
936 dev 192
  }
193
 
194
  protected void setEntryInfo(IndexEntry entry, File file, boolean small)
1242 dev 195
    throws IOException, SAXException
936 dev 196
  {
197
    String title = file.getName();
198
    int[]  size;
199
    String path  = getPath(file);
200
 
201
    if(file.isDirectory()) {
202
      size = thumbnailer.getDirSize(file);
203
    }
204
    else {
1242 dev 205
      title = FileUtils.extractFileName(title);
936 dev 206
 
207
      if(small)
208
        size = thumbnailer.getSmallSize(file);
1242 dev 209
      else
936 dev 210
        size = thumbnailer.getMediumSize(file);
211
    }
212
 
213
    entry.setFile(file);
214
    entry.setPath(path == null ? null : URLEncoder.encode(path, URL_ENCODING));
215
    entry.setTitle(title);
216
    entry.setIsDir(file.isDirectory());
217
    entry.setWidth(size[0]);
218
    entry.setHeight(size[1]);
1242 dev 219
 
220
    MetaInfoItem meta = findMetaInfo(imagesRoot, file);
221
    if(meta != null) {
222
      if(meta.getTitle() != null) {
223
        entry.setTitle(meta.getTitle());
224
      }
225
      if(meta.getSubtitle() != null) {
226
        entry.setSubtitle(meta.getSubtitle());
227
        entry.setSubtitleMime(meta.getSubtitleMime());
228
      }
229
      if(meta.getComment() != null) {
230
        entry.setComment(meta.getComment());
231
        entry.setCommentMime(meta.getCommentMime());
232
      }
233
    }
936 dev 234
  }
235
 
236
  public String getThumbnailMime()
237
  {
238
    return thumbnailer.getMime();
239
  }
240
 
241
  public String getOriginMime(String path)
242
    throws IOException, LogicException
243
  {
244
    File file = new File(imagesRoot, path);
245
 
246
    if(!file.exists()) return null;
247
    securePath(imagesRoot, file);
248
 
249
    return FileUtils.getMime(FileUtils.extractFileExt(path));
250
  }
251
 
252
  public void writeDir(String path, OutputStream out)
253
    throws IOException, LogicException
254
  {
255
    File file = new File(imagesRoot, path);
256
 
257
    securePath(imagesRoot, file);
258
    thumbnailer.writeDir(file, out);
259
  }
260
 
261
  public void writeSmall(String path, OutputStream out)
262
    throws IOException, LogicException
263
  {
264
    File file = new File(imagesRoot, path);
265
 
266
    securePath(imagesRoot, file);
267
    thumbnailer.writeSmall(file, out);
268
  }
269
 
270
  public void writeMedium(String path, OutputStream out)
271
    throws IOException, LogicException
272
  {
273
    File file = new File(imagesRoot, path);
274
 
275
    securePath(imagesRoot, file);
276
    thumbnailer.writeMedium(file, out);
277
  }
278
 
279
  public void writeOrigin(String path, OutputStream out)
280
    throws IOException, LogicException
281
  {
282
    FileInputStream in   = null;
283
    File            file = new File(imagesRoot, path);
284
 
285
    securePath(imagesRoot, file);
286
 
287
    try {
288
      in  = new FileInputStream(file);
289
      FileUtils.copyStreams(in, out);
290
    }
291
    finally {
292
      if(in != null) in.close();
293
    }
294
  }
295
 
1242 dev 296
  protected MetaInfo getMetaInfo(File dir)
297
    throws IOException, SAXException
298
  {
299
    MetaInfo meta = (MetaInfo)metaInfos.get(dir);
300
    if(meta != null) return meta;
301
 
302
    File metaFile = new File(dir, META_FILE_NAME);
303
    if(!metaFile.exists()) return null;
304
 
305
    meta = (MetaInfo)metaDigester.parse(new FileInputStream(metaFile));
306
    meta.setDir(dir);
307
    metaInfos.put(dir, meta);
308
 
309
    return meta;
310
  }
311
 
312
  protected MetaInfoItem findMetaInfo(File rootDir, File file)
313
    throws IOException, SAXException
314
  {
315
    file    = file.getCanonicalFile();
316
    rootDir = rootDir.getCanonicalFile();
317
 
318
    File dir = file;
319
    if(!dir.isDirectory())
320
      dir = dir.getParentFile();
321
 
322
    MetaInfoItem metaItem = null;
323
    for(; metaItem == null; dir = dir.getParentFile()) {
324
      if(dir == null) break;
325
 
326
      MetaInfo meta = getMetaInfo(dir);
327
      if(meta != null) {
328
        metaItem = meta.findItem(file);
329
      }
330
      if(rootDir.equals(dir)) break;
331
    }
332
 
333
    return metaItem;
334
  }
335
 
336
  public boolean listDirectory(String dirName, int page, List table, List pages)
337
    throws IOException, LogicException, SAXException
338
  {
936 dev 339
    File dir = new File(imagesRoot, dirName);
340
 
341
    securePath(imagesRoot, dir);
1242 dev 342
    if(!dir.exists()) return false;
936 dev 343
 
1242 dev 344
    File[] children  = dir.listFiles(imagesFilter);
345
    int    pos       = page * columns * rows;
936 dev 346
 
347
    Arrays.sort(children, fileNameComparator);
1242 dev 348
    metaInfos.clear();           // FIXME do this more intelligent (?)
936 dev 349
 
1242 dev 350
    // the pages list
351
    pages.clear();
352
    for(int i = 0; i < (int)Math.ceil((double)children.length / columns / rows); i++) {
353
      pages.add(new PageItem(i, i == page));
354
    }
355
 
356
    // the main table
357
    table.clear();
358
    while(pos < children.length && pos < (page+1) * columns * rows) {
936 dev 359
      List row    = new ArrayList();
360
      int  rowPos = 0;
361
 
1242 dev 362
      table.add(row);
936 dev 363
 
364
      while(rowPos < columns && pos < children.length) {
365
        String path  = getPath(children[pos]);
366
        String title = children[pos].getName();
367
        int[]  size;
368
 
369
        if(children[pos].isDirectory()) {
370
          size  = thumbnailer.getDirSize(children[pos]);
371
        }
372
        else {
373
          size  = thumbnailer.getSmallSize(children[pos]);
374
          title = FileUtils.extractFileName(title);
375
        }
376
 
1242 dev 377
        IndexEntry entry = new IndexEntry(children[pos],
936 dev 378
          URLEncoder.encode(path, URL_ENCODING),
1242 dev 379
          title, children[pos].isDirectory(), size[0], size[1]);
380
 
381
        MetaInfoItem meta = findMetaInfo(imagesRoot, children[pos]);
382
        if(meta != null) {
383
          if(meta.getTitle() != null) {
384
            entry.setTitle(meta.getTitle());
385
          }
386
          if(meta.getSubtitle() != null) {
387
            entry.setSubtitle(meta.getSubtitle());
388
            entry.setSubtitleMime(meta.getSubtitleMime());
389
          }
390
          if(meta.getComment() != null) {
391
            entry.setComment(meta.getComment());
392
            entry.setCommentMime(meta.getCommentMime());
393
          }
394
        }
395
 
396
        row.add(entry);
936 dev 397
        rowPos++;
398
        pos++;
399
      }
400
 
401
      while(rowPos < columns) {
402
        row.add(null);
403
        rowPos++;
404
      }
405
    }
406
 
1242 dev 407
    return true;
936 dev 408
  }
409
 
410
  protected String getPath(File file)
411
    throws IOException
412
  {
413
    String path     = file.getCanonicalPath();
414
    String rootPath = imagesRoot.getCanonicalPath();
415
 
416
    if(path.equals(rootPath)) return "";
417
    if(!rootPath.endsWith(File.separator)) rootPath += File.separator;
418
 
419
    if(!path.startsWith(rootPath))
420
      return null;
421
 
422
    return path.substring(rootPath.length());
423
  }
424
 
425
  protected ThumbnailPosition[] parseThumbnailPositions(String str)
426
  {
427
    List            list      = new ArrayList();
428
    StringTokenizer tokenizer = new StringTokenizer(str, ";");
429
 
430
    while(tokenizer.hasMoreTokens()) {
431
      StringTokenizer tokenParser
432
        = new StringTokenizer(tokenizer.nextToken(), ",");
433
      int    x            = Integer.parseInt(tokenParser.nextToken().trim());
434
      int    y            = Integer.parseInt(tokenParser.nextToken().trim());
435
      int    width        = Integer.parseInt(tokenParser.nextToken().trim());
436
      int    height       = Integer.parseInt(tokenParser.nextToken().trim());
437
      String horAlignStr  = tokenParser.nextToken().trim().toLowerCase();
438
      String vertAlignStr = tokenParser.nextToken().trim().toLowerCase();
439
      int    horAlign;
440
      int    vertAlign;
441
 
442
      if("l".equals(horAlignStr))
443
        horAlign = ThumbnailPosition.ALIGN_HOR_LEFT;
444
      else if("r".equals(horAlignStr))
445
        horAlign = ThumbnailPosition.ALIGN_HOR_RIGHT;
446
      else if("c".equals(horAlignStr))
447
        horAlign = ThumbnailPosition.ALIGN_HOR_CENTER;
448
      else
449
        throw new RuntimeException(
450
          "Cannot parse " + horAlignStr + " as horizontal alignment");
451
 
452
      if("t".equals(vertAlignStr))
453
        vertAlign = ThumbnailPosition.ALIGN_VERT_TOP;
454
      else if("b".equals(vertAlignStr))
455
        vertAlign = ThumbnailPosition.ALIGN_VERT_BOTTOM;
456
      else if("c".equals(vertAlignStr))
457
        vertAlign = ThumbnailPosition.ALIGN_VERT_CENTER;
458
      else
459
        throw new RuntimeException(
460
          "Cannot parse " + vertAlignStr + " as vertical alignment");
461
 
462
      list.add(new ThumbnailPosition(x, y, width, height, horAlign, vertAlign));
463
    }
464
 
465
    ThumbnailPosition[] res = new ThumbnailPosition[list.size()];
466
 
467
    for(int i = 0; i < res.length; i++)
468
      res[i] = (ThumbnailPosition)list.get(i);
469
 
470
    return res;
471
  }
472
 
473
  protected static final Logic instance = new Logic();
474
 
475
  public static Logic getLogic()
476
  {
477
    return instance;
478
  }
479
 
480
  /**
481
   * checks if given file is really under the parent directory
482
   */
483
  protected void securePath(File parentDir, File file)
484
    throws IOException, LogicException
485
  {
486
    if(parentDir == null || file == null) return;
487
 
488
    File partFile = file.getCanonicalFile();
489
 
490
    parentDir = parentDir.getCanonicalFile();
491
    while(partFile != null) {
492
      if(partFile.equals(parentDir)) return;
493
      partFile = partFile.getParentFile();
494
    }
495
 
496
    throw new LogicSecurityException(
497
      "[" + file.getCanonicalPath() + "] is outside of directory ["
498
      + parentDir.getCanonicalPath() + "]");
499
  }
500
}