/* * This file is part of Caliph & Emir. * * Caliph & Emir is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Caliph & Emir is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Caliph & Emir; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright statement: * -------------------- * (c) 2002-2005 by Mathias Lux (mathias@juggle.at) * http://www.juggle.at, http://caliph-emir.sourceforge.net */ package at.lux.fotoretrieval.retrievalengines; import at.lux.components.StatusBar; import at.lux.fotoretrieval.FileOperations; import at.lux.fotoretrieval.ResultListEntry; import at.lux.fotoretrieval.RetrievalToolkit; import at.lux.fotoretrieval.lucene.*; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.WhitespaceAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import javax.swing.*; import java.io.File; import java.io.IOException; import java.io.FileInputStream; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.*; /** * Date: 25.03.2005 * Time: 23:58:46 * * @author Mathias Lux, mathias@juggle.at */ public class LucenePathIndexRetrievalEngine extends AbstractRetrievalEngine { private static Namespace xsi = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); private int maxResults = 20; private IndexSearcher indexSearcher; public LucenePathIndexRetrievalEngine(int maxResults) { this.maxResults = maxResults; } public List<ResultListEntry> getImagesBySemantics(String xPath, Vector objects, String whereToSearch, boolean recursive, JProgressBar progress) { if (progress != null) { progress.setString("Query expansion running"); } List<Graph> graphList = getExpandedGraphs(xPath, whereToSearch); LinkedList<ResultListEntry> results = new LinkedList<ResultListEntry>(); if (progress != null) { progress.setMinimum(0); progress.setMaximum(graphList.size()); progress.setValue(0); progress.setString("Querying expanded graphs"); } int countGraph = 0; try { indexSearcher = new IndexSearcher(parsePathIndexDirectory(whereToSearch)); for (Graph graph : graphList) { results.addAll(searchForGraph(graph, whereToSearch)); countGraph++; if (progress != null) progress.setValue(countGraph); } indexSearcher.close(); } catch (IOException e) { e.printStackTrace(); } Collections.sort(results); LinkedList<ResultListEntry> sorted = new LinkedList<ResultListEntry>(); HashSet<String> doublettes = new HashSet<String>(); for (ResultListEntry entry : results) { String descriptionPath = entry.getDescriptionPath(); if (!doublettes.contains(descriptionPath)) { doublettes.add(descriptionPath); sorted.add(entry); } } return sorted.subList(0, Math.min(sorted.size(), maxResults)); } public List<ResultListEntry> searchForGraph(Graph graph, String whereToSearch) { LinkedList<ResultListEntry> results = new LinkedList<ResultListEntry>(); String query = createLucenePathQuery(graph); try { QueryParser qParse = new QueryParser("paths", new WhitespaceAnalyzer()); Query q = qParse.parse(query); Hits hits = indexSearcher.search(q); SAXBuilder builder = new SAXBuilder(); int maxResults = Math.min(hits.length(), this.maxResults); for (int i = 0; i< maxResults; i++) { String[] filenames = hits.doc(i).getValues("file"); for (String fileName : filenames) { Element e = builder.build(new FileInputStream(fileName)).getRootElement(); ResultListEntry entry = new ResultListEntry((double) hits.score(i), e, fileName); results.add(entry); } } } catch (IOException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (JDOMException e) { e.printStackTrace(); } return results; } private List<Graph> getExpandedGraphs(String query, String whereToSearch) { List<String> nodeQueries = new LinkedList<String>(); StringTokenizer st = new StringTokenizer(query, "]"); String relationString = ""; List<Relation> relations = new LinkedList<Relation>(); while (st.hasMoreTokens()) { String s = st.nextToken().trim(); if (s.startsWith("[")) { s = s.substring(1); nodeQueries.add(s); } else { relationString = s; } } if (relationString.length() > 1) { // there are relations, go ahead and parse them: StringTokenizer sr = new StringTokenizer(relationString); Relation currentRelation = null; while (sr.hasMoreTokens()) { String s = sr.nextToken(); try { int i = Integer.parseInt(s); if (currentRelation.getSource() < 0) { currentRelation.setSource(i); } else if (currentRelation.getTarget() < 0) { currentRelation.setTarget(i); currentRelation.eliminateInverse(); relations.add(currentRelation); currentRelation = null; } } catch (NumberFormatException e) { // its the type :) currentRelation = new Relation(-1, -1, s.trim()); } } } // so for now do the retrieval for the nodes: int numOfNodes = nodeQueries.size(); List<List<Node>> nodeResults = new LinkedList<List<Node>>(); for (int i = 0; i < numOfNodes; i++) { String queryString = nodeQueries.get(i); List<Node> nodes; if (!queryString.equals("*")) { nodes = getNodes(queryString, whereToSearch); } else { nodes = new LinkedList<Node>(); nodes.add(new Node(-1, 1f, "*")); } nodeResults.add(nodes); } // now we can expand our query on retrieved nodes: List<Graph> graphList = getExpandedGraphsFromResults(nodeResults, relations, 5); return graphList; } private List<Graph> getExpandedGraphsFromResults(List<List<Node>> nodeResults, List<Relation> relations, int depth) { List<List<Node>> expanded = getExpandedSets(nodeResults, depth); List<Graph> results = new LinkedList<Graph>(); for (List<Node> nodes : expanded) { Graph g = getGraphFromResults(nodes, relations); results.add(g); } return results; } private Graph getGraphFromResults(List<Node> nodeResults, List<Relation> relations) { HashMap<Integer, Integer> idReplacementTable = new HashMap<Integer, Integer>(nodeResults.size()); List<Node> nodes = new LinkedList<Node>(); List<Relation> myRelations = new LinkedList<Relation>(); for (int i = 0; i < nodeResults.size(); i++) { Node node = nodeResults.get(i); idReplacementTable.put(i + 1, node.getNodeID()); nodes.add(node); } // Create the relations with the real IDs: for (Relation r : relations) { int src = (idReplacementTable.get(r.getSource())); int tgt = (idReplacementTable.get(r.getTarget())); myRelations.add(new Relation(src, tgt, r.getType())); } // now we can create the graph we want to search for: return new Graph(nodes, myRelations); } private List<List<Node>> getExpandedSets(List<List<Node>> nodeResults, int depth) { if (nodeResults.size() > 1) { List<Node> firstNodesResults = nodeResults.get(0); int numLevels = 0; for (Node node : firstNodesResults) { if (node.getWeight() < 1f) break; numLevels++; } numLevels += depth; if (firstNodesResults.size() < depth) { numLevels = firstNodesResults.size(); } List<List<Node>> tmpNodeResults = new LinkedList<List<Node>>(nodeResults); tmpNodeResults.remove(0); List<List<Node>> results = getExpandedSets(tmpNodeResults, depth); List<List<Node>> endResult = new LinkedList<List<Node>>(); for (int i = 0; i < numLevels && i < firstNodesResults.size(); i++) { for (List<Node> result : results) { List<Node> nodeList = new LinkedList<Node>(result); nodeList.add(0, firstNodesResults.get(i)); endResult.add(nodeList); } } return endResult; } else { List<List<Node>> endResult = new LinkedList<List<Node>>(); List<Node> firstNodesResults = nodeResults.get(0); int numLevels = 0; for (Node node : firstNodesResults) { if (node.getWeight() < 1f) break; numLevels++; } numLevels += depth; for (int i = 0; i < numLevels && i < firstNodesResults.size(); i++) { List<Node> nodeList = new LinkedList<Node>(); nodeList.add(firstNodesResults.get(i)); endResult.add(nodeList); } return endResult; } } /** * Searches for all available nodes with given query String * * @param queryString query like "Mathias Lux" or some text inside a node. * @param whereToSearch defines the base directory for the search * @return a List of Matching nodes with their associated weights */ public List<Node> getNodes(String queryString, String whereToSearch) { LinkedList<Node> result = new LinkedList<Node>(); try { IndexSearcher searcher = new IndexSearcher(parseSemanticIndexDirectory(whereToSearch)); String[] fieldsToSearchInForNodes = new String[] {"label", "givenname", "organization", "familyname", "all"}; MultiFieldQueryParser qParse = new MultiFieldQueryParser(fieldsToSearchInForNodes, new StandardAnalyzer()); Query query = qParse.parse(queryString); Hits hits = searcher.search(query); int hitsCount = hits.length(); if (hitsCount > maxResults) hitsCount = maxResults; for (int i = 0; i < hitsCount; i++) { Document d = hits.doc(i); StringBuilder sb = new StringBuilder(20); sb.append(hits.score(i)); sb.append(": "); sb.append(d.get("label")); Node node = new Node(Integer.parseInt(d.get("id")), hits.score(i), d.get("label")); node.setType(d.get("type")); result.add(node); } } catch (IOException e) { e.printStackTrace(); } catch (ParseException e) { System.err.println("QueryString was: " + queryString); e.printStackTrace(); } return result; } public List<ResultListEntry> getSimilarImages(Element VisualDescriptor, String whereToSearch, boolean recursive, JProgressBar progress) { throw new UnsupportedOperationException("Not Implemented!"); } public List<ResultListEntry> getImagesByXPathSearch(String xPath, String whereToSearch, boolean recursive, JProgressBar progress) { throw new UnsupportedOperationException("Not Implemented!"); } public void indexFilesSemantically(String pathToIndex, StatusBar statusBar) { if (statusBar != null) statusBar.setStatus("Creating index from semantic annotations"); SAXBuilder builder = new SAXBuilder(); XMLOutputter outputter = new XMLOutputter(Format.getRawFormat().setIndent("").setLineSeparator("").setExpandEmptyElements(false)); try { String[] descriptions = FileOperations.getAllDescriptions(new File(pathToIndex), true); if (descriptions == null) return; float numAllDocsPercent = (float) descriptions.length / 100f; DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(); df.setMaximumFractionDigits(1); // Preparing objects for the index: HashMap<String, ElementEntry> elementMap = new HashMap<String, ElementEntry>(descriptions.length); HashMap<Element, LinkedList<String>> element2document = new HashMap<Element, LinkedList<String>>(descriptions.length); // in the first run we identify the semantic objects that we want to index and build // a table were we can relate them to the documents (identified by their path) for (int i = 0; i < descriptions.length; i++) { try { Element e = builder.build(new FileInputStream(descriptions[i])).getRootElement(); List l = RetrievalToolkit.xpathQuery(e, "//Semantic/SemanticBase", null); for (Object aL : l) { Element semanticElement = (Element) aL; String xmlString = outputter.outputString(semanticElement).trim().replaceAll("id=\"id_[0-9]*\"", ""); // check if element is already there, indicator is its string representation. if (!elementMap.keySet().contains(xmlString)) { // its not here, put it in. elementMap.put(xmlString, new ElementEntry(semanticElement, elementMap.size())); // System.out.println(xmlString); } // now get the unified element semanticElement = elementMap.get(xmlString).semanticElement; // and check if there is an entry in the table for where to find the element if (!element2document.keySet().contains(semanticElement)) { element2document.put(semanticElement, new LinkedList<String>()); } // and add found document if not already there: List documentList = element2document.get(semanticElement); if (!documentList.contains(descriptions[i])) documentList.add(descriptions[i]); } if (statusBar != null) statusBar.setStatus("Parsing documents for nodes: " + df.format((float) i / numAllDocsPercent)); } catch (JDOMException e1) { System.err.println("Exception in document #" + i + ": " + e1.getMessage()); } catch (IOException e1) { e1.printStackTrace(); } } // read stats: // System.out.println("Got " + countOverallElements + " Elements in " + descriptions.length + " descriptions, " + elementMap.size() + " elements are pairwise different."); // Now we can add the nodes to a lucene index: // fields: label, id, type, files (separated by '|'), xml, all // ------------------------------------------- // opening the index for writing: boolean createFlag = true; String indexDir = parseSemanticIndexDirectory(pathToIndex); Analyzer analyzer = new StandardAnalyzer(); IndexWriter writer = new IndexWriter(indexDir, analyzer, createFlag); if (statusBar != null) statusBar.setStatus("Creating index for " + element2document.size() + " different available nodes"); // iterating through nodes and storing them: for (Element semElement : element2document.keySet()) { // needed for later XPath :( otherwise everthing in the whole document is retrieved. String fileList = getFileListFromNode(element2document.get(semElement)); Document idxDocument = new Document(); // adding the file itself ... idxDocument.add(new Field("files", fileList, Field.Store.YES, Field.Index.NO)); // System.out.println(((Element) o).getTextTrim()); // StringBuilder all = new StringBuilder(255); // adding the label // addToDocument(idxDocument, semElement, "//Label/Name", "label", all); String elementLabel = semElement.getChild("Label", semElement.getNamespace()).getChildTextTrim("Name", semElement.getNamespace()); Field labelField = new Field("label", elementLabel, Field.Store.YES, Field.Index.TOKENIZED); labelField.setBoost(1.2f); idxDocument.add(labelField); // adding the type: String elementType = semElement.getAttribute("type", xsi).getValue().trim(); idxDocument.add(new Field("type", elementType, Field.Store.YES, Field.Index.NO)); // adding the XML contents: String xmlString = outputter.outputString(semElement); idxDocument.add(new Field("xml", xmlString, Field.Store.YES, Field.Index.NO)); // adding the id: idxDocument.add(new Field("id", elementMap.get(xmlString.trim().replaceAll("id=\"id_[0-9]*\"", "")).id + "", Field.Store.YES, Field.Index.UN_TOKENIZED)); // TODO: split the indexing for objects based on type: // adding all, unstored for retrieval only if (elementType.equals("AgentObjectType")) { createIndexDocumentFromSemanticAgent(semElement, idxDocument); } else if (elementType.equals("EventType")) { createIndexDocumentFromSemanticElement(semElement, idxDocument); } else if (elementType.equals("SemanticPlaceType")) { createIndexDocumentFromSemanticElement(semElement, idxDocument); } else if (elementType.equals("SemanticTimeType")) { createIndexDocumentFromSemanticElement(semElement, idxDocument); } else { createIndexDocumentFromSemanticElement(semElement, idxDocument); } writer.addDocument(idxDocument); } // now optimize and close the index: // todo: open index for appending and/or updating writer.optimize(); writer.close(); // Now we can create the powerset for each existing graph // (based on sorted node ids) and store // all resulting graphs within an index. // ---------------------------------------------------------- if (statusBar != null) statusBar.setStatus("Creating and merging of available graphs"); HashMap<Graph, HashSet<String>> graph2document = new HashMap<Graph, HashSet<String>>(descriptions.length); for (int i = 0; i < descriptions.length; i++) try { Element e = builder.build(new FileInputStream(descriptions[i])).getRootElement(); List l = RetrievalToolkit.xpathQuery(e, "//Semantic/SemanticBase", null); HashMap<String, Integer> docID2overallID = new HashMap<String, Integer>(l.size()); LinkedList<Relation> relations = new LinkedList<Relation>(); LinkedList<Integer> nodes = new LinkedList<Integer>(); for (Object aL : l) { Element semanticElement = (Element) aL; String xmlString = outputter.outputString(semanticElement); int id = elementMap.get(xmlString.trim().replaceAll("id=\"id_[0-9]*\"", "")).id; String docID = semanticElement.getAttribute("id").getValue(); docID2overallID.put(docID, id); nodes.add(id); } // get all relations with global ids and eliminate inverse relations l = RetrievalToolkit.xpathQuery(e, "//Graph/Relation", null); for (Object aL1 : l) { Element relation = (Element) aL1; int source = docID2overallID.get(relation.getAttribute("source").getValue().substring(1)); int target = docID2overallID.get(relation.getAttribute("target").getValue().substring(1)); String type = relation.getAttribute("type").getValue(); type = type.substring(type.lastIndexOf(':') + 1); Relation r = eliminateInverse(new Relation(source, target, type)); relations.add(r); } // now create a graph object Collections.sort(nodes); Collections.sort(relations); LinkedList<Node> nodeList = new LinkedList<Node>(); for (Integer node : nodes) { nodeList.add(new Node(node)); } Graph g = new Graph(nodeList, relations); HashSet<String> docs = new HashSet<String>(1); docs.add(descriptions[i]); graph2document.put(g, docs); } catch (JDOMException e1) { System.err.println(new StringBuilder().append("Exception in document #").append(i).append(": ").append(e1.getMessage()).toString()); } HashMap<String, Graph> str2graph = new HashMap<String, Graph>(graph2document.size() / 2); HashMap<Graph, HashSet<String>> g2d = new HashMap<Graph, HashSet<String>>(descriptions.length); /* For now we reduce the number of graphs by identifiying and merging duplicates and remove redundant entries: */ for (Graph g : graph2document.keySet()) { if (str2graph.containsKey(g.toString())) { g2d.get(str2graph.get(g.toString())).addAll(graph2document.get(g)); } else { str2graph.put(g.toString(), g); g2d.put(g, graph2document.get(g)); } } graph2document = g2d; System.out.println(graph2document.size() + " non trivial different graphs were found"); // now put all the available graphs into an index: // ----------------------------------------------- // for now we will store a simple text file: if (statusBar != null) statusBar.setStatus("Saving index of paths"); boolean createPathIndexFlag = true; String pathIndexDir = parsePathIndexDirectory(pathToIndex); IndexWriter pathIndexWriter = new IndexWriter(pathIndexDir, new GraphAnalyzer(), createPathIndexFlag); for (Graph graph : graph2document.keySet()) { HashSet<String> files = graph2document.get(graph); Document idxDocument = new Document(); // adding the file itself ... for (String s : files) { idxDocument.add(new Field("file", s, Field.Store.YES, Field.Index.NO)); } // adding the graph ... idxDocument.add(new Field("graph", graph.toString(), Field.Store.YES, Field.Index.TOKENIZED)); // idxDocument.add(Field.UnIndexed("graph", graph.toString())); // adding the paths StringBuilder sb = new StringBuilder(256); sb.append(graph.toString()); List<Path> pathList = (new LabeledGraph(graph)).get2Paths(); if (!pathList.isEmpty()) sb.append(' '); for (Iterator<Path> iterator1 = pathList.iterator(); iterator1.hasNext();) { Path path = iterator1.next(); sb.append(path.toString()); if (iterator1.hasNext()) sb.append(' '); } idxDocument.add(new Field("paths", sb.toString(), Field.Store.YES, Field.Index.TOKENIZED)); pathIndexWriter.addDocument(idxDocument); } // now optimize and close the index: pathIndexWriter.optimize(); pathIndexWriter.close(); } catch (IOException e) { e.printStackTrace(); } } private static void createIndexDocumentFromSemanticElement(Element semElement, Document idxDocument) { StringBuilder all = new StringBuilder(64); List l = RetrievalToolkit.xpathQuery(semElement, "*//*", null); for (Object aL : l) { Element e = (Element) aL; all.append(e.getTextTrim()); all.append(' '); } Field field = new Field("all", all.toString(), Field.Store.NO, Field.Index.TOKENIZED); field.setBoost(0.8f); idxDocument.add(field); } private static void createIndexDocumentFromSemanticAgent(Element e, Document idxDocument) { Namespace mpeg7 = e.getNamespace(); StringBuilder all = new StringBuilder(64); Element name = (Element) e.getChild("Agent", mpeg7).getChild("Name", mpeg7); idxDocument.add(new Field("givenname", name.getChild("GivenName", mpeg7).getTextTrim(), Field.Store.YES, Field.Index.TOKENIZED)); idxDocument.add(new Field("familyname", name.getChild("FamilyName", mpeg7).getTextTrim(), Field.Store.YES, Field.Index.TOKENIZED)); Element affiliation = e.getChild("Agent", mpeg7).getChild("Affiliation", mpeg7); if (affiliation != null) { String organization = affiliation.getChild("Organization", mpeg7).getChildText("Name", mpeg7); if (organization != null && organization.length() > 0) { Field field = new Field("organization", organization, Field.Store.YES, Field.Index.TOKENIZED); field.setBoost(0.5f); idxDocument.add(field); } } } /** * Creates and returns index directory for node index ... * * @param pathToIndex * @return the directory to the semantic index */ public static String parseSemanticIndexDirectory(String pathToIndex) { String indexDir = pathToIndex; if (!indexDir.endsWith(System.getProperty("file.separator"))) indexDir += System.getProperty("file.separator"); indexDir += "idx_semantic"; File indexDirFile = new File(indexDir); if (!indexDirFile.exists()) indexDirFile.mkdir(); return indexDir; } /** * Creates and returns the index directory for path index ... * * @param pathToIndex * @return the directory to the index */ public static String parsePathIndexDirectory(String pathToIndex) { String indexDir = pathToIndex; if (!indexDir.endsWith(System.getProperty("file.separator"))) indexDir += System.getProperty("file.separator"); indexDir += "idx_paths"; File indexDirFile = new File(indexDir); if (!indexDirFile.exists()) indexDirFile.mkdir(); return indexDir; } /** * Creates and returns the index directory for 2-path index ... * * @param pathToIndex * @return * @deprecated */ public static String parse2PathIndexDirectory(String pathToIndex) { String indexDir = pathToIndex; if (!indexDir.endsWith(System.getProperty("file.separator"))) indexDir += System.getProperty("file.separator"); indexDir += "idx_2paths"; File indexDirFile = new File(indexDir); if (!indexDirFile.exists()) indexDirFile.mkdir(); return indexDir; } private static String getFileListFromNode(Collection<String> list) { StringBuilder files = new StringBuilder(64); for (Iterator<String> it2 = list.iterator(); it2.hasNext();) { files.append(it2.next()); if (it2.hasNext()) { files.append('|'); } } return files.toString(); } /** * Eliminates all inverse relations to simplify retrieval * * @param relation * @return the normalized relation */ public static Relation eliminateInverse(Relation relation) { Relation result = relation; if (Relation.relationMapping.containsKey(relation.getType())) { result = new Relation(relation.getTarget(), relation.getSource(), Relation.relationMapping.get(relation.getType())); } return result; } /** * Creates a query String from a graph ... * @param g * @return a path query for lucene */ public static String createLucenePathQuery(Graph g) { StringBuilder sb = new StringBuilder(256); List<Node> nodes = g.getNodes(); Collections.sort(nodes); List<Relation> relations = g.getRelations(); Collections.sort(relations); for (Node n : nodes) { // if no anonymous node: if (n.getNodeID() > -1) { sb.append('_'); sb.append(n.getNodeID()); sb.append(' '); } } for (Relation relation : relations) { sb.append(' '); sb.append(createLucenePathQuery(relation)); sb.append(' '); } // 2-path generation: LabeledGraph lg = new LabeledGraph(g); sb.append(create2PathQuery(lg)); return sb.toString().trim(); } public static String create2PathQuery(LabeledGraph lg) { StringBuilder sw = new StringBuilder(64); List<Path> paths = lg.get2Paths(); for (Path p : paths) { sw.append(createPathQueryString(p)); sw.append(' '); } return sw.toString(); } public static String createPathQueryString(Path p) { StringBuilder sw = new StringBuilder(128); LinkedList<String> nodes = p.getNodes(); LinkedList<String> relations = p.getRelations(); if (nodes.getFirst().equals('*') || nodes.getLast().equals('*')) { // we have to implement both directions. sw.append('('); sw.append('_'); for (int i = 0; i < nodes.size(); i++) { sw.append(deAnonymize(nodes.get(i))); if (i < nodes.size() - 1) { sw.append('_'); sw.append(relations.get(i)); sw.append('_'); } } sw.append(" OR _"); for (int i = nodes.size() - 1; i >= 0; i--) { sw.append(deAnonymize(nodes.get(i))); if (i > 0) { sw.append('_'); sw.append(Relation.invertRelationType(relations.get(i - 1))); sw.append('_'); } } sw.append(')'); } else { // we have to implement only one direction :) sw.append('_'); // if the first is smaller then do not change the order if (nodes.getFirst().compareTo(nodes.getLast()) < 0) { for (int i = 0; i < nodes.size(); i++) { sw.append(deAnonymize(nodes.get(i))); if (i < nodes.size() - 1) { sw.append('_'); sw.append(relations.get(i)); sw.append('_'); } } } else { // switch order of nodes .. for (int i = nodes.size() - 1; i >= 0; i--) { sw.append(deAnonymize(nodes.get(i))); if (i > 0) { sw.append('_'); sw.append(Relation.invertRelationType(relations.get(i - 1))); sw.append('_'); } } } } return sw.toString(); } public static String createLucenePathQuery(Relation r) { StringBuilder sb = new StringBuilder(64); if (r.getType().equals("*")) { String source = deAnonymize(r.getSource()); String target = deAnonymize(r.getTarget()); sb.append('('); sb.append("_*_"); sb.append(source); sb.append('_'); sb.append(target); sb.append(" OR "); sb.append("_*_"); sb.append(target); sb.append('_'); sb.append(source); sb.append(')'); } else { // check if not inverse :) r.eliminateInverse(); sb.append('_'); sb.append(r.getType()); sb.append('_'); sb.append(deAnonymize(r.getSource())); sb.append('_'); sb.append(deAnonymize(r.getTarget())); } return sb.toString().trim(); } private static String deAnonymize(int nodeID) { if (nodeID > -1) return String.valueOf(nodeID); else return "*"; } private static String deAnonymize(String nodeID) { if (!nodeID.equals("-1")) return (nodeID); else return "*"; } public List<ResultListEntry> getSimilarImages_fromSet(Set<Element> VisualDescriptorSet, String whereToSearch, boolean recursive, JProgressBar progress) { throw new UnsupportedOperationException("Not Implemented!"); } }