View Javadoc

1   /*  Copyright 2004 Brian McCallister
2    *
3    *   Licensed under the Apache License, Version 2.0 (the "License");
4    *   you may not use this file except in compliance with the License.
5    *   You may obtain a copy of the License at
6    *
7    *       http://www.apache.org/licenses/LICENSE-2.0
8    *
9    *   Unless required by applicable law or agreed to in writing, software
10   *   distributed under the License is distributed on an "AS IS" BASIS,
11   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   *   See the License for the specific language governing permissions and
13   *   limitations under the License.
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 }