1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.skife.lucene.graph;
16
17 import org.apache.commons.beanutils.PropertyUtils;
18 import org.apache.commons.grafolia.graph.Graph;
19 import org.apache.commons.grafolia.graph.Visitor;
20 import org.apache.commons.grafolia.graph.Walker;
21 import org.apache.commons.logging.LogFactory;
22 import org.apache.lucene.document.Document;
23 import org.apache.lucene.document.Field;
24 import org.apache.lucene.index.IndexWriter;
25
26 import java.beans.PropertyDescriptor;
27 import java.io.IOException;
28 import java.lang.reflect.Method;
29 import java.util.ArrayList;
30 import java.util.HashSet;
31 import java.util.Iterator;
32
33 /***
34 * Not Thread Safe
35 */
36 class IndexBuilder implements Runnable
37 {
38 private final MetadataFactory metadata;
39 private final ObjectIndexFilter filter;
40 private final IndexWriter writer;
41 private final Graph graph;
42 private final NameMapper names;
43 private final ValueMapper values;
44 private final HashSet visited = new HashSet();
45 private final HashSet documents = new HashSet();
46
47 /***
48 * Be sure to call <code>run</code> after instantiation to build
49 * the actual index!
50 */
51 IndexBuilder(final Graph graph,
52 final MetadataFactory metadata,
53 final ObjectIndexFilter filter,
54 final IndexWriter writer,
55 final NameMapper names,
56 final ValueMapper values)
57 {
58 this.metadata = metadata;
59 this.filter = filter;
60 this.writer = writer;
61 this.graph = graph;
62 this.names = names;
63 this.values = values;
64 }
65
66 /***
67 * Build the actual index
68 */
69 public void run()
70 {
71 final PropertyChain chain = new PropertyChain(names, values);
72 graph.visit(new Visitor()
73 {
74 public void visit(final Walker walker)
75 {
76 final Object current = walker.getCurrent();
77 if (skip(current))
78 {
79 return;
80 }
81 else if (visited(current))
82 {
83 chain.reVisited(current);
84 }
85 if (filter.include(current))
86 {
87 visited.add(current);
88
89 final Field[] metadata_fields = metadata.build(current);
90 final Field[] bean_fields = extractPropertyFields(current);
91
92 final Document doc = new Document();
93 for (int i = 0; i != metadata_fields.length; i++) doc.add(metadata_fields[i]);
94 for (int i = 0; i != bean_fields.length; i++) doc.add(bean_fields[i]);
95
96 documents.add(doc);
97
98 chain.visited(current, doc);
99 walker.visitReferences();
100 chain.pop();
101 }
102 else
103 {
104 walker.visitReferences();
105 }
106 }
107 });
108 visited.clear();
109 chain.clear();
110
111 for (Iterator iterator = documents.iterator(); iterator.hasNext();)
112 {
113 final Document document = (Document) iterator.next();
114 try
115 {
116 writer.addDocument(document);
117 }
118 catch (IOException e)
119 {
120 LogFactory.getLog(IndexBuilder.class).error("Unable to add document: " + e.getMessage(), e);
121 }
122 }
123 }
124
125 /***
126 * Have we traversed this instance already?
127 */
128 private boolean visited(final Object current)
129 {
130 return visited.contains(current);
131 }
132
133 /***
134 * Should we skip (not traverse) this instance?
135 */
136 private boolean skip(final Object current)
137 {
138 return current == null
139 || current.getClass().isPrimitive()
140 || Graph.isImmutableType(current);
141 }
142
143 /***
144 * Returns true if the property should be indexed, false otherwise.
145 * Basically properties defined on standard library base classes and
146 * write-only properties
147 */
148 public static boolean isIndexedProperty(final PropertyDescriptor property)
149 {
150 final Method a_method = property.getReadMethod();
151 if (a_method == null)
152 {
153 return false;
154 }
155 else if (a_method.getDeclaringClass().getPackage().getName().startsWith("java"))
156 {
157 return false;
158 }
159 return true;
160 }
161
162 /***
163 * Find the simple property fields, a la ('name, 'identity, 'color)
164 */
165 private Field[] extractPropertyFields(final Object current)
166 {
167 final PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(current);
168 final int property_count = properties.length;
169 final ArrayList fields = new ArrayList(property_count);
170 for (int i = 0; i != property_count; ++i)
171 {
172 final PropertyDescriptor property = properties[i];
173 if (!isIndexedProperty(property)) continue;
174 final String name = property.getName();
175 final String value;
176 try
177 {
178 value = values.build(PropertyUtils.getProperty(current, name));
179 }
180 catch (Exception e)
181 {
182 LogFactory.getLog(IndexBuilder.class).error("Unable to index property: " + e.getMessage(), e);
183 continue;
184 }
185
186 final Field field = Field.Text(name, value);
187 fields.add(field);
188 }
189 return (Field[]) fields.toArray(new Field[fields.size()]);
190 }
191 }