From 7eeb7d9bae5081b625a50629d60b2cc72b29dab7 Mon Sep 17 00:00:00 2001 From: Knut Forkalsrud Date: Sat, 23 Oct 2010 14:01:21 -0700 Subject: [PATCH] Initial work on keyword search --- .../forkalsrud/album/exif/DirectoryEntry.java | 36 ++-------- .../java/org/forkalsrud/album/exif/Entry.java | 12 ++++ .../album/exif/EntryWithChildren.java | 66 +++++++++++++++++++ .../forkalsrud/album/exif/SearchEngine.java | 58 ++++++++++++++++ .../forkalsrud/album/exif/SearchEntry.java | 23 +++++++ .../forkalsrud/album/exif/SearchResults.java | 39 +++++++++++ .../forkalsrud/album/web/AlbumServlet.java | 32 +++++++++ 7 files changed, 234 insertions(+), 32 deletions(-) create mode 100644 src/main/java/org/forkalsrud/album/exif/EntryWithChildren.java create mode 100644 src/main/java/org/forkalsrud/album/exif/SearchEngine.java create mode 100644 src/main/java/org/forkalsrud/album/exif/SearchEntry.java create mode 100644 src/main/java/org/forkalsrud/album/exif/SearchResults.java diff --git a/src/main/java/org/forkalsrud/album/exif/DirectoryEntry.java b/src/main/java/org/forkalsrud/album/exif/DirectoryEntry.java index c6f7871..3eaea46 100644 --- a/src/main/java/org/forkalsrud/album/exif/DirectoryEntry.java +++ b/src/main/java/org/forkalsrud/album/exif/DirectoryEntry.java @@ -8,7 +8,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; @@ -22,14 +21,13 @@ import org.forkalsrud.album.db.DirectoryProps; /** * @author knut */ -public class DirectoryEntry extends Entry { +public class DirectoryEntry extends EntryWithChildren { final static String CACHE_FILE = "cache.properties"; final static String OVERRIDE_FILE = "album.properties"; DirectoryDatabase db; DirectoryProps cache; - List children = new ArrayList(); boolean childrenLoaded = false; Comparator sort = null; Date earliest = null; @@ -53,14 +51,10 @@ public class DirectoryEntry extends Entry { @Override - public boolean isFile() { - return false; - } - public List getContents() { loadChildren(); - return children; + return super.getContents(); } @Override @@ -75,18 +69,10 @@ public class DirectoryEntry extends Entry { return super.getCaption(); } - public void addContents(Entry entry) { - children.add(entry); - } - + @Override public Entry get(File f) { loadChildren(); - for (Entry e : children) { - if (f.equals(e.file)) { - return e; - } - } - return null; + return super.get(f); } protected synchronized void loadChildren() { @@ -139,20 +125,6 @@ public class DirectoryEntry extends Entry { Collections.sort(children, sort); } - void fillLinkedList() { - - Entry prev = null; - for (Entry e : children) { - e.prev = prev; - if (prev != null) { - prev.next = e; - } - prev = e; - } - if (prev != null) { - prev.next = null; - } - } private void populate(DirectoryProps props) throws ParseException { diff --git a/src/main/java/org/forkalsrud/album/exif/Entry.java b/src/main/java/org/forkalsrud/album/exif/Entry.java index c076e64..022b4ca 100644 --- a/src/main/java/org/forkalsrud/album/exif/Entry.java +++ b/src/main/java/org/forkalsrud/album/exif/Entry.java @@ -31,6 +31,18 @@ public abstract class Entry { this.file = file; } + protected Entry(Entry other) { + this(other.file); + this.thumbnail = other.thumbnail; + this.caption = other.caption; + this.date = other.date; + /* + this.next = other.next; + this.prev = other.prev; + this.parent = other.parent; + */ + } + public abstract boolean isFile(); diff --git a/src/main/java/org/forkalsrud/album/exif/EntryWithChildren.java b/src/main/java/org/forkalsrud/album/exif/EntryWithChildren.java new file mode 100644 index 0000000..03c350e --- /dev/null +++ b/src/main/java/org/forkalsrud/album/exif/EntryWithChildren.java @@ -0,0 +1,66 @@ +/** + * + */ +package org.forkalsrud.album.exif; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author knut + * + */ +public class EntryWithChildren extends Entry { + + protected List children = new ArrayList(); + + protected EntryWithChildren(Entry root) { + super(root); + } + + protected EntryWithChildren(File path) { + super(path); + } + + /* (non-Javadoc) + * @see org.forkalsrud.album.exif.Entry#isFile() + */ + @Override + public boolean isFile() { + return false; + } + + void fillLinkedList() { + + Entry prev = null; + for (Entry e : children) { + e.prev = prev; + if (prev != null) { + prev.next = e; + } + prev = e; + } + if (prev != null) { + prev.next = null; + } + } + + public List getContents() { + + return children; + } + + public void addContents(Entry entry) { + children.add(entry); + } + + public Entry get(File f) { + for (Entry e : children) { + if (f.equals(e.file)) { + return e; + } + } + return null; + } +} diff --git a/src/main/java/org/forkalsrud/album/exif/SearchEngine.java b/src/main/java/org/forkalsrud/album/exif/SearchEngine.java new file mode 100644 index 0000000..a87bc8a --- /dev/null +++ b/src/main/java/org/forkalsrud/album/exif/SearchEngine.java @@ -0,0 +1,58 @@ +package org.forkalsrud.album.exif; + +import java.util.LinkedList; + +public class SearchEngine { + + DirectoryEntry root; + SearchResults results; + LinkedList queue; + + + public SearchEngine(DirectoryEntry root) { + this.root = root; + this.results = new SearchResults(root); + this.queue = new LinkedList(); + this.queue.add(root); + } + + public SearchResults search(String query) { + + while (!queue.isEmpty()) { + DirectoryEntry next = queue.removeFirst(); + processDirectory(next, query); + } + results.fillLinkedList(); + return results; + } + + + void processDirectory(DirectoryEntry dir, String query) { + + tryAttribute(dir, dir.getName(), query); + tryAttribute(dir, dir.getCaption(), query); + tryAttribute(dir, dir.getPath().getAbsolutePath(), query); + + for (Entry e : dir.getContents()) { + if (!e.isFile()) { + queue.add((DirectoryEntry)e); + continue; + } + FileEntry file = (FileEntry)e; + tryAttribute(file, file.getName(), query); + tryAttribute(file, file.getCaption(), query); + tryAttribute(file, file.getPath().getAbsolutePath(), query); + } + } + + void tryAttribute(Entry entry, String attr, String query) { + if (isMatch(attr, query)) { + results.addMatch(entry); + } + } + + boolean isMatch(String candidate, String query) { + return candidate != null && candidate.indexOf(query) >= 0; + } + +} diff --git a/src/main/java/org/forkalsrud/album/exif/SearchEntry.java b/src/main/java/org/forkalsrud/album/exif/SearchEntry.java new file mode 100644 index 0000000..7fab023 --- /dev/null +++ b/src/main/java/org/forkalsrud/album/exif/SearchEntry.java @@ -0,0 +1,23 @@ +package org.forkalsrud.album.exif; + + +public class SearchEntry extends Entry { + + protected int score; + + protected SearchEntry(SearchResults parent, Entry entry) { + super(entry); + this.parent = parent; + this.score = 1; + } + + @Override + public boolean isFile() { + return true; + } + + public void addScore() { + this.score += 1; + } + +} diff --git a/src/main/java/org/forkalsrud/album/exif/SearchResults.java b/src/main/java/org/forkalsrud/album/exif/SearchResults.java new file mode 100644 index 0000000..14db095 --- /dev/null +++ b/src/main/java/org/forkalsrud/album/exif/SearchResults.java @@ -0,0 +1,39 @@ +package org.forkalsrud.album.exif; + +import java.io.File; +import java.util.Comparator; +import java.util.HashMap; + + +public class SearchResults extends EntryWithChildren { + + protected HashMap dupes = new HashMap(); + Comparator sort = new Comparator() { + + public int compare(SearchEntry e1, SearchEntry e2) { + + return Integer.valueOf(e2.score).compareTo(e1.score); + } + }; + + + protected SearchResults(DirectoryEntry root) { + super(root); + } + + public void addMatch(Entry entry) { + + File path = entry.getPath(); + SearchEntry existing = dupes.get(path); + if (existing == null) { + existing = new SearchEntry(this, entry); + dupes.put(path, existing); + children.add(existing); + } else { + existing.addScore(); + } + } + + + +} diff --git a/src/main/java/org/forkalsrud/album/web/AlbumServlet.java b/src/main/java/org/forkalsrud/album/web/AlbumServlet.java index 1a57b07..83ecb7a 100644 --- a/src/main/java/org/forkalsrud/album/web/AlbumServlet.java +++ b/src/main/java/org/forkalsrud/album/web/AlbumServlet.java @@ -27,6 +27,8 @@ import org.forkalsrud.album.db.ThumbnailDatabase; import org.forkalsrud.album.exif.DirectoryEntry; import org.forkalsrud.album.exif.Entry; import org.forkalsrud.album.exif.FileEntry; +import org.forkalsrud.album.exif.SearchEngine; +import org.forkalsrud.album.exif.SearchResults; import org.forkalsrud.album.exif.Thumbnail; import org.springframework.web.util.HtmlUtils; @@ -207,6 +209,12 @@ public class AlbumServlet handleEdit(req, res, (FileEntry)resolveEntry(pathInfo)); return; } + + if (pathInfo.endsWith(".search")) { + pathInfo = pathInfo.substring(0, pathInfo.length() - ".search".length()); + handleSearch(req, res, (DirectoryEntry)resolveEntry(pathInfo)); + return; + } File file = new File(base, pathInfo); if (!file.canRead()) { @@ -283,6 +291,27 @@ public class AlbumServlet } } + void handleSearch(HttpServletRequest req, HttpServletResponse res, DirectoryEntry entry) { + try { + String query = req.getParameter("q"); + + SearchEngine search = new SearchEngine(entry); + SearchResults results = search.search(query); + + res.setContentType("text/html"); + req.setAttribute("entry", results); + req.setAttribute("thmb", new Integer(250)); + req.setAttribute("full", new Integer(800)); + RequestDispatcher rd = req.getRequestDispatcher("/WEB-INF/velocity/photo.vm"); + rd.forward(req, res); + } catch (Exception ex) { + throw new RuntimeException("sadness", ex); + } + } + + + + boolean etagMatches(HttpServletRequest req, String fileEtag) { String cacheControl = req.getHeader("Cache-Control"); @@ -401,6 +430,9 @@ public class AlbumServlet Calendar cal = Calendar.getInstance(); public String year(Date d) { + if (d == null) { + return ""; + } cal.setTime(d); return String.valueOf(cal.get(Calendar.YEAR)); }