1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package net.sf.ginp.browser;
22
23 import java.awt.geom.AffineTransform;
24 import java.awt.image.AffineTransformOp;
25 import java.awt.image.BufferedImage;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.io.LineNumberReader;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.Vector;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41
42 import net.sf.ginp.PicCollection;
43 import net.sf.ginp.util.StringTool;
44 import net.sf.ginp.config.Configuration;
45
46
47 import com.sun.image.codec.jpeg.JPEGCodec;
48 import com.sun.image.codec.jpeg.JPEGImageDecoder;
49 import com.sun.image.codec.jpeg.JPEGImageEncoder;
50
51 /**
52 * @author Justin Sher
53 */
54 public class FolderManagerImpl implements FolderManager {
55
56 private static long MAX_CACHE=60000;
57 private static MakeThumbs mth=null;
58 private static Thread mthThread=null;
59 HashMap folderCache=new HashMap();
60 HashMap picCache=new HashMap();
61 long folderCacheTimeout=-1;
62 long picCacheTimeout=-1;
63 private static Log log = LogFactory.getLog(FolderManagerImpl.class);
64 /**
65 *Gets the foldersInDirectory attribute of the PicCollection object
66 *@param root the root directory
67 *@param relPath Description of the Parameter
68 *@return The foldersInDirectory value
69 */
70 private String[] getFoldersInDirectory(String root,String relPath) {
71 long duration=System.currentTimeMillis()-picCacheTimeout;
72 if (picCacheTimeout<0 || duration>MAX_CACHE) {
73 synchronized(folderCache) {
74
75
76 folderCacheTimeout=System.currentTimeMillis();
77 folderCache.clear();
78 }
79 }
80 synchronized (folderCache) {
81 if (folderCache.get(root+relPath)!=null) {
82 return (String[]) folderCache.get(root+relPath);
83 }
84 }
85 Vector dirs = new Vector();
86
87 File dir = new File(root + relPath);
88 if (dir.isDirectory()) {
89 String[] files = dir.list();
90 Arrays.sort(files);
91 for (int i = 0; i < files.length; i++) {
92 File file = new File(root + relPath + "/" + files[i]);
93 if (file.isDirectory()) {
94 if (!(files[i].startsWith("."))) {
95 dirs.add(files[i]);
96 }
97 }
98 }
99 }
100 String[] retFolders = new String[dirs.size()];
101 for (int i = 0; i < dirs.size(); i++) {
102 retFolders[i] = relPath + (String) dirs.get(i);
103 }
104 Arrays.sort(retFolders);
105 synchronized (folderCache) {
106 folderCache.put(root+relPath,retFolders);
107 }
108 return retFolders;
109 }
110
111 /**
112 * Gets the picturesInDirectory attribute of the PicCollection object
113 *
114 *@param relPath Description of the Parameter
115 *@return The picturesInDirectory value
116 */
117 private String[] getPicturesInDirectory(String root,String relPath) {
118 long duration=System.currentTimeMillis()-picCacheTimeout;
119 if (picCacheTimeout<0 || duration>MAX_CACHE) {
120 synchronized(picCache) {
121
122
123 picCacheTimeout=System.currentTimeMillis();
124 picCache.clear();
125 }
126 }
127 synchronized (picCache) {
128 if (picCache.get(root+relPath)!=null) {
129 String cached[]= (String[]) picCache.get(root+relPath);
130 return cached;
131 }
132 }
133 Vector pics = new Vector();
134 try {
135 File dir = new File(root + relPath);
136 if (dir.isDirectory()) {
137 String[] files = dir.list();
138 for (int i = 0; i < files.length; i++) {
139 File file = new File(root + relPath + "/" + files[i]);
140 if ((!file.isDirectory()) && ((files[i].toLowerCase()).endsWith(".jpg") || (files[i].toLowerCase()).endsWith(".jpeg"))) {
141 pics.add(files[i]);
142 if (log.isDebugEnabled()) {
143 log.debug("Adding picture file: " + files[i]);
144 }
145 }
146 }
147
148
149 File fl = new File(root + relPath + "ginpfolder.xml");
150 if (fl.exists()) {
151 FileReader fr = new FileReader(fl);
152 LineNumberReader lr = new LineNumberReader(fr);
153 StringBuffer sb = new StringBuffer();
154 String temp;
155 while ((temp = lr.readLine()) != null) {
156 sb.append(temp + "\n");
157 }
158 String featuredpicsXML = StringTool.getXMLTagContent("featuredpics", sb.toString());
159 String[] picsXML = StringTool.splitToArray(featuredpicsXML, "<pic>");
160
161 for (int i = 0; i < picsXML.length; i++) {
162 if (picsXML[i].indexOf("</pic>") != -1) {
163 temp = picsXML[i].substring(0, picsXML[i].indexOf("</pic>"));
164 fl = new File(root + relPath + temp);
165 if (fl.exists()) {
166 pics.add(temp);
167 }
168 }
169 }
170 }
171 }
172 } catch (IOException ex) {
173 log.error(ex);
174 }
175 String[] retPictures = new String[pics.size()];
176 for (int i = 0; i < pics.size(); i++) {
177 retPictures[i] = (String) pics.get(i);
178 }
179 String[] sorted = sortPictures(retPictures);
180 synchronized (picCache) {
181 if (picCache.get(root+relPath)==null) {
182 picCache.put(root+relPath,sorted);
183 }
184 else {
185 return (String[]) picCache.get(root+relPath);
186 }
187 }
188
189
190 synchronized(MakeThumbs.class) {
191 if (mth==null || (mthThread!=null && !mthThread.isAlive())) {
192 mth = new MakeThumbs();
193 mthThread = new Thread(mth);
194 mthThread.setDaemon(true);
195 mthThread.setPriority(Thread.MIN_PRIORITY);
196 mthThread.start();
197 }
198 }
199 mth.addToQueue(root+relPath, sorted);
200 return sorted;
201 }
202
203 /**
204 * Sort the arry of picture names.
205 *
206 *@param ary Array of unSorted Picture Names
207 *@return Sorted array based on current Sort criteria.
208 */
209 public String[] sortPictures(String[] ary) {
210
211
212
213
214 Comparator comp = new ComparePictures();
215 Arrays.sort(ary, comp);
216
217 return ary;
218 }
219
220
221
222
223
224
225 /**
226 * Description of the Class
227 *
228 *@author doc
229 *@version $Revision: 303 $
230 */
231 class ComparePictures implements Comparator {
232
233
234 /**
235 * Constructor for the ComparePictures object
236 */
237 ComparePictures() { }
238
239
240 /**
241 * Description of the Method
242 *
243 *@param o1 Description of the Parameter
244 *@param o2 Description of the Parameter
245 *@return Description of the Return Value
246 */
247 public int compare(Object o1, Object o2) {
248
249 if (o1.toString().indexOf("/") != -1) {
250 o1 = o1.toString().substring(o1.toString().lastIndexOf("/") + 1);
251 }
252 if (o2.toString().indexOf("/") != -1) {
253 o2 = o2.toString().substring(o2.toString().lastIndexOf("/") + 1);
254 }
255 return o1.toString().compareTo(o2.toString());
256 }
257
258
259 /**
260 * Description of the Method
261 *
262 *@param obj Description of the Parameter
263 *@return Description of the Return Value
264 */
265 public boolean equals(Object obj) {
266 return true;
267 }
268 }
269
270
271
272
273
274 public int getFoldersLength(PicCollection collection) {
275 return this.getFoldersInDirectory(collection.getRoot(),collection.getPath()).length;
276 }
277
278
279
280
281 public int getPicturesInDirectoryLength(PicCollection collection,String selectedFolder) {
282 return this.getPicturesInDirectory(collection,selectedFolder).length;
283 }
284
285
286
287
288 public String[] getFoldersInDirectory(PicCollection collection,String string) {
289 return this.getFoldersInDirectory(collection.getRoot(),string);
290 }
291
292
293
294
295 public String[] getPicturesInDirectory(PicCollection collection,String path) {
296 return this.getPicturesInDirectory(collection.getRoot(),path);
297 }
298
299
300
301
302 public String getPrevPictureName(String picName, PicCollection collection) {
303 String[] pictures2 = this.getPictures(collection);
304 if (pictures2[0].equals(picName)) { return null; }
305 for (int x=1;x<pictures2.length;x++) {
306 String pic=pictures2[x];
307 if (pic.equals(picName)) {
308 return pictures2[--x];
309 }
310 }
311 return null;
312 }
313
314
315
316
317 public String getNextPictureName(String picName, PicCollection collection) {
318 String[] pictures2 = this.getPictures(collection);
319 if (pictures2[pictures2.length-1].equals(picName)) { return null; }
320 for (int x=0;x<pictures2.length-1;x++) {
321 String pic=pictures2[x];
322 if (pic.equals(picName)) {
323 return pictures2[++x];
324 }
325 }
326 return null;
327 }
328
329
330
331
332 public int getPicturesLength(PicCollection collection,String path) {
333 return this.getPicturesInDirectory(collection,path).length;
334 }
335
336
337
338
339 public String[] getPictures(PicCollection collection) {
340 return this.getPicturesInDirectory(collection.getRoot(),collection.getPath());
341 }
342
343
344
345
346 public int getPicturesLength(PicCollection collection) {
347 return this.getPicturesInDirectory(collection.getRoot(),collection.getPath()).length;
348 }
349
350
351
352
353 public String getFolder(PicCollection collection, int count) {
354 return this.getFoldersInDirectory(collection.getRoot(),collection.getPath())[count];
355 }
356
357
358 }
359
360 /**
361 * A class that makes Thumbnail images. This is designed to be used as a
362 * background thread.
363 *
364 *@author $Author: dougculnane $
365 *@version $Revision: 303 $
366 */
367 class MakeThumbs implements Runnable {
368
369
370 ArrayList thumbQueue=new ArrayList();
371 private Log log = LogFactory.getLog(MakeThumbs.class);
372
373 /**
374 * Add a request to make thumbs for a particular directory
375 * @param path the path of the directory
376 * @param pics the pictures in the directory
377 */
378 public void addToQueue(String path, String[] pics) {
379 synchronized(thumbQueue) {
380 Iterator iter=thumbQueue.iterator();
381 while (iter.hasNext()) {
382 Object[] queue=(Object[])iter.next();
383 if (queue[0].equals(path)) {
384
385 return;
386 }
387 }
388 thumbQueue.add(new Object[] { path , pics } );
389 }
390 }
391
392
393 /**
394 * Main processing method for the MakeThumbs object
395 */
396 public void run() {
397
398 String url=null;
399 String[] pics=null;
400 int[] thumbSizes = {Configuration.getThumbSize(),
401 Configuration.getFilmStripThumbSize()};
402
403 while (true) {
404 boolean makeQueue=false;
405 synchronized (thumbQueue) {
406 if (thumbQueue.size()>0) {
407 Object[] queue=(Object[]) thumbQueue.remove(0);
408 url=(String) queue[0];
409 pics=(String[]) queue[1];
410 makeQueue=true;
411 }
412 }
413 if (!makeQueue){
414 try {
415 Thread.sleep(1000);
416 continue;
417 } catch (InterruptedException e1) {
418
419 log.error(e1);
420
421 }
422 }
423
424 try {
425 File folder = new File(url);
426 if (folder.exists()) {
427
428 File ginpFolder = new File(url + "/.ginp");
429 if (!(ginpFolder.exists())) {
430 ginpFolder.mkdir();
431 }
432
433 for (int i = 0; i < pics.length; i++) {
434 makeThumbImage(url, pics[i], thumbSizes);
435 }
436 }
437 } catch (Exception e) {
438 log.error(e);
439 }
440 }
441 }
442
443 void makeThumbImage(String url, String fileName, int[] sizes) {
444
445 if (log.isDebugEnabled()) {
446 log.debug("makeThumbImage: url=" + url
447 + " fileName=" + fileName + " int[] sizes");
448 }
449
450 for (int i = 0; i < sizes.length; i++) {
451
452 try {
453
454 String thumbFile = getThumbFileName(url, sizes[i], fileName);
455 File ginpPic = new File(thumbFile);
456 File origPic = new File(url + "/" + fileName);
457
458 boolean makeThumb = false;
459 if (ginpPic.exists()) {
460
461 if (origPic.lastModified() > ginpPic.lastModified()) {
462 makeThumb = true;
463 }
464 } else {
465 makeThumb = true;
466 }
467 if (makeThumb) {
468 if (i != 0) {
469 File bigThumb = new File(getThumbFileName(url, sizes[i-1], fileName));
470 if (bigThumb.exists()) {
471 origPic = bigThumb;
472 }
473 }
474 makeThumbImage(origPic, thumbFile, sizes[i]);
475 }
476 } catch (Exception e) {
477 log.error(e);
478 }
479 }
480 }
481
482 String getThumbFileName(String url, int size, String fileName){
483
484 String retFileName = url + "/.ginp/" + size + "-" + fileName;
485
486
487 if (fileName.indexOf("/") != -1) {
488 retFileName = url
489 + fileName.substring(0, fileName.lastIndexOf("/"))
490 + "/.ginp/" + size + "-"
491 + fileName.substring(fileName.lastIndexOf("/") + 1);
492 }
493
494 return retFileName;
495 }
496
497
498
499 void makeThumbImage(File origPicture, String thumbFileName, int maxThumbSize) {
500
501 if (log.isDebugEnabled()) {
502 log.debug("makeThumbImage: origFileName=" + origPicture.getAbsolutePath()
503 + " thumbFileName=" + thumbFileName + " maxThumbSize=" + maxThumbSize);
504 }
505
506
507 if ((origPicture.getName().toLowerCase()).endsWith(".jpg")
508 || (origPicture.getName().toLowerCase()).endsWith(".jpeg")) {
509 try {
510
511
512 JPEGImageDecoder dc = JPEGCodec.createJPEGDecoder((new
513 FileInputStream(origPicture)));
514 BufferedImage origImage = dc.decodeAsBufferedImage();
515 int origHeight = origImage.getHeight(null);
516 int origWidth = origImage.getWidth(null);
517 int scaledW = 0;
518 int scaledH = 0;
519 double scale = 1.0;
520
521 if (origHeight < origWidth) {
522 scale = (double) maxThumbSize / (double) origWidth;
523 } else {
524 scale = (double) maxThumbSize / (double) origHeight;
525 }
526 scaledW = (int) (scale * origWidth);
527 scaledH = (int) (scale * origHeight);
528
529
530 AffineTransform tx;
531 AffineTransformOp af;
532 JPEGImageEncoder encoder;
533 BufferedImage outImage;
534
535 outImage = new BufferedImage(scaledW, scaledH,
536 BufferedImage.TYPE_INT_RGB);
537 tx = new AffineTransform();
538 tx.scale(scale, scale);
539 af = new AffineTransformOp(tx, null);
540 af.filter(origImage, outImage);
541
542 File ginpFolder = new File(thumbFileName.substring(0, thumbFileName.lastIndexOf("/.ginp")) + "/.ginp");
543 if (!(ginpFolder.exists())) {
544 ginpFolder.mkdir();
545 }
546 encoder = JPEGCodec.createJPEGEncoder(new FileOutputStream(thumbFileName));
547 encoder.encode(outImage);
548 } catch (Exception e) {
549 log.error("Error Makeing Thumb Image " + thumbFileName, e);
550 }
551 }
552 }
553 }