Initial work on keyword search
This commit is contained in:
parent
5149259970
commit
7eeb7d9bae
7 changed files with 234 additions and 32 deletions
|
|
@ -8,7 +8,6 @@ import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -22,14 +21,13 @@ import org.forkalsrud.album.db.DirectoryProps;
|
||||||
/**
|
/**
|
||||||
* @author knut
|
* @author knut
|
||||||
*/
|
*/
|
||||||
public class DirectoryEntry extends Entry {
|
public class DirectoryEntry extends EntryWithChildren {
|
||||||
|
|
||||||
final static String CACHE_FILE = "cache.properties";
|
final static String CACHE_FILE = "cache.properties";
|
||||||
final static String OVERRIDE_FILE = "album.properties";
|
final static String OVERRIDE_FILE = "album.properties";
|
||||||
|
|
||||||
DirectoryDatabase db;
|
DirectoryDatabase db;
|
||||||
DirectoryProps cache;
|
DirectoryProps cache;
|
||||||
List<Entry> children = new ArrayList<Entry>();
|
|
||||||
boolean childrenLoaded = false;
|
boolean childrenLoaded = false;
|
||||||
Comparator<Entry> sort = null;
|
Comparator<Entry> sort = null;
|
||||||
Date earliest = null;
|
Date earliest = null;
|
||||||
|
|
@ -53,14 +51,10 @@ public class DirectoryEntry extends Entry {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFile() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Entry> getContents() {
|
public List<Entry> getContents() {
|
||||||
|
|
||||||
loadChildren();
|
loadChildren();
|
||||||
return children;
|
return super.getContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -75,18 +69,10 @@ public class DirectoryEntry extends Entry {
|
||||||
return super.getCaption();
|
return super.getCaption();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addContents(Entry entry) {
|
@Override
|
||||||
children.add(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entry get(File f) {
|
public Entry get(File f) {
|
||||||
loadChildren();
|
loadChildren();
|
||||||
for (Entry e : children) {
|
return super.get(f);
|
||||||
if (f.equals(e.file)) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void loadChildren() {
|
protected synchronized void loadChildren() {
|
||||||
|
|
@ -139,20 +125,6 @@ public class DirectoryEntry extends Entry {
|
||||||
Collections.sort(children, sort);
|
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 {
|
private void populate(DirectoryProps props) throws ParseException {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,18 @@ public abstract class Entry {
|
||||||
this.file = file;
|
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();
|
public abstract boolean isFile();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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<Entry> children = new ArrayList<Entry>();
|
||||||
|
|
||||||
|
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<Entry> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/main/java/org/forkalsrud/album/exif/SearchEngine.java
Normal file
58
src/main/java/org/forkalsrud/album/exif/SearchEngine.java
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.forkalsrud.album.exif;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class SearchEngine {
|
||||||
|
|
||||||
|
DirectoryEntry root;
|
||||||
|
SearchResults results;
|
||||||
|
LinkedList<DirectoryEntry> queue;
|
||||||
|
|
||||||
|
|
||||||
|
public SearchEngine(DirectoryEntry root) {
|
||||||
|
this.root = root;
|
||||||
|
this.results = new SearchResults(root);
|
||||||
|
this.queue = new LinkedList<DirectoryEntry>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
23
src/main/java/org/forkalsrud/album/exif/SearchEntry.java
Normal file
23
src/main/java/org/forkalsrud/album/exif/SearchEntry.java
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
src/main/java/org/forkalsrud/album/exif/SearchResults.java
Normal file
39
src/main/java/org/forkalsrud/album/exif/SearchResults.java
Normal file
|
|
@ -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<File, SearchEntry> dupes = new HashMap<File, SearchEntry>();
|
||||||
|
Comparator<SearchEntry> sort = new Comparator<SearchEntry>() {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,8 @@ import org.forkalsrud.album.db.ThumbnailDatabase;
|
||||||
import org.forkalsrud.album.exif.DirectoryEntry;
|
import org.forkalsrud.album.exif.DirectoryEntry;
|
||||||
import org.forkalsrud.album.exif.Entry;
|
import org.forkalsrud.album.exif.Entry;
|
||||||
import org.forkalsrud.album.exif.FileEntry;
|
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.forkalsrud.album.exif.Thumbnail;
|
||||||
import org.springframework.web.util.HtmlUtils;
|
import org.springframework.web.util.HtmlUtils;
|
||||||
|
|
||||||
|
|
@ -207,6 +209,12 @@ public class AlbumServlet
|
||||||
handleEdit(req, res, (FileEntry)resolveEntry(pathInfo));
|
handleEdit(req, res, (FileEntry)resolveEntry(pathInfo));
|
||||||
return;
|
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);
|
File file = new File(base, pathInfo);
|
||||||
if (!file.canRead()) {
|
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) {
|
boolean etagMatches(HttpServletRequest req, String fileEtag) {
|
||||||
|
|
||||||
String cacheControl = req.getHeader("Cache-Control");
|
String cacheControl = req.getHeader("Cache-Control");
|
||||||
|
|
@ -401,6 +430,9 @@ public class AlbumServlet
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
|
|
||||||
public String year(Date d) {
|
public String year(Date d) {
|
||||||
|
if (d == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
cal.setTime(d);
|
cal.setTime(d);
|
||||||
return String.valueOf(cal.get(Calendar.YEAR));
|
return String.valueOf(cal.get(Calendar.YEAR));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue