~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 
 * contributor license agreements.  See the NOTICE file distributed with
4
 
 * this work for additional information regarding copyright ownership.
5
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 
 * (the "License"); you may not use this file except in compliance with
7
 
 * the License.  You may obtain a copy of the License at
8
 
 *
9
 
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 
 *
11
 
 * Unless required by applicable law or agreed to in writing, software
12
 
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 
 * See the License for the specific language governing permissions and
15
 
 * limitations under the License.
16
 
 */
17
 
package org.apache.solr.client.solrj.beans;
18
 
 
19
 
import org.apache.solr.common.SolrDocumentList;
20
 
import org.apache.solr.common.SolrDocument;
21
 
import org.apache.solr.common.SolrInputDocument;
22
 
 
23
 
import java.lang.reflect.*;
24
 
import java.util.*;
25
 
import java.util.regex.Pattern;
26
 
import java.util.concurrent.ConcurrentHashMap;
27
 
import java.nio.ByteBuffer;
28
 
 
29
 
/**
30
 
 * A class to map objects to and from solr documents.
31
 
 * 
32
 
 * @version $Id: DocumentObjectBinder.java 945270 2010-05-17 17:45:18Z rmuir $
33
 
 * @since solr 1.3
34
 
 */
35
 
public class DocumentObjectBinder {
36
 
  private final Map<Class, List<DocField>> infocache = new ConcurrentHashMap<Class, List<DocField>>();
37
 
 
38
 
  public DocumentObjectBinder() {
39
 
  }
40
 
 
41
 
  public <T> List<T> getBeans(Class<T> clazz, SolrDocumentList solrDocList) {
42
 
    List<DocField> fields = getDocFields( clazz );
43
 
    List<T> result = new ArrayList<T>(solrDocList.size());
44
 
 
45
 
    for(int j=0;j<solrDocList.size();j++) {
46
 
      SolrDocument sdoc = solrDocList.get(j);
47
 
          result.add(getBean(clazz, fields, sdoc));
48
 
    }
49
 
    return result;
50
 
  }
51
 
  public <T> T getBean(Class<T> clazz, SolrDocument solrDoc) {
52
 
    return getBean(clazz, null,solrDoc);
53
 
  }
54
 
  
55
 
  private <T> T getBean(Class<T> clazz, List<DocField> fields, SolrDocument solrDoc) {
56
 
    if (fields == null) {
57
 
      fields = getDocFields(clazz);
58
 
    }
59
 
    T obj = null;
60
 
    try {
61
 
      obj = clazz.newInstance();
62
 
    } catch (Exception e) {
63
 
      throw new RuntimeException("Could not instantiate object of " + clazz, e);
64
 
    }
65
 
    for (int i = 0; i < fields.size(); i++) {
66
 
      DocField docField = fields.get(i);
67
 
      docField.inject(obj, solrDoc);
68
 
    }
69
 
    return obj;
70
 
  }
71
 
  
72
 
  public SolrInputDocument toSolrInputDocument( Object obj )
73
 
  {
74
 
    List<DocField> fields = getDocFields( obj.getClass() );
75
 
    if( fields.isEmpty() ) {
76
 
      throw new RuntimeException( "class: "+obj.getClass()+" does not define any fields." );
77
 
    }
78
 
    
79
 
    SolrInputDocument doc = new SolrInputDocument();
80
 
        for (DocField field : fields) {
81
 
                if (field.dynamicFieldNamePatternMatcher != null
82
 
                                && field.get(obj) != null && field.isContainedInMap) {
83
 
                        Map<String, Object> mapValue = (HashMap<String, Object>) field
84
 
                                        .get(obj);
85
 
 
86
 
                        for (Map.Entry<String, Object> e : mapValue.entrySet()) {
87
 
                                doc.setField( e.getKey(), e.getValue(), 1.0f);
88
 
                        }
89
 
                } else {
90
 
                        doc.setField(field.name, field.get(obj), 1.0f);
91
 
                }
92
 
        }
93
 
    return doc;
94
 
  }
95
 
  
96
 
  private List<DocField> getDocFields( Class clazz )
97
 
  {
98
 
    List<DocField> fields = infocache.get(clazz);
99
 
    if (fields == null) {
100
 
      synchronized(infocache) {
101
 
        infocache.put(clazz, fields = collectInfo(clazz));
102
 
      }
103
 
    }
104
 
    return fields;
105
 
  }
106
 
 
107
 
  private List<DocField> collectInfo(Class clazz) {
108
 
    List<DocField> fields = new ArrayList<DocField>();
109
 
    Class superClazz = clazz;
110
 
    ArrayList<AccessibleObject> members = new ArrayList<AccessibleObject>();
111
 
    while (superClazz != null && superClazz != Object.class) {
112
 
      members.addAll(Arrays.asList(superClazz.getDeclaredFields()));
113
 
      members.addAll(Arrays.asList(superClazz.getDeclaredMethods()));
114
 
      superClazz = superClazz.getSuperclass();
115
 
    }
116
 
    for (AccessibleObject member : members) {
117
 
      if (member.isAnnotationPresent(Field.class)) {
118
 
        member.setAccessible(true);
119
 
        fields.add(new DocField(member));
120
 
      }
121
 
    }
122
 
    return fields;
123
 
  }
124
 
 
125
 
  private static class DocField {
126
 
    private String name;
127
 
    private java.lang.reflect.Field field;
128
 
    private Method setter;
129
 
    private Method getter;
130
 
    private Class type;
131
 
    private boolean isArray = false, isList=false;
132
 
 
133
 
    /*
134
 
     * dynamic fields may use a Map based data structure to bind a given field.
135
 
     * if a mapping is done using, "Map<String, List<String>> foo", <code>isContainedInMap</code>
136
 
     * is set to <code>TRUE</code> as well as <code>isList</code> is set to <code>TRUE</code>
137
 
     */
138
 
    boolean isContainedInMap =false;
139
 
    private Pattern dynamicFieldNamePatternMatcher;
140
 
 
141
 
    public DocField(AccessibleObject member) {
142
 
      if (member instanceof java.lang.reflect.Field) {
143
 
        field = (java.lang.reflect.Field) member;
144
 
      } else {
145
 
        setter = (Method) member;
146
 
      }
147
 
      Field annotation = member.getAnnotation(Field.class);
148
 
      storeName(annotation);
149
 
      storeType();
150
 
      
151
 
      // Look for a matching getter
152
 
      if( setter != null ) {
153
 
        String gname = setter.getName();
154
 
        if( gname.startsWith("set") ) {
155
 
          gname = "get" + gname.substring(3);
156
 
          try {
157
 
            getter = setter.getDeclaringClass().getMethod( gname, (Class[])null );
158
 
          }
159
 
          catch( Exception ex ) {
160
 
            // no getter -- don't worry about it...
161
 
            if( type == Boolean.class ) {
162
 
              gname = "is" + setter.getName().substring( 3 );
163
 
              try {
164
 
                getter = setter.getDeclaringClass().getMethod( gname, (Class[])null );
165
 
              }
166
 
              catch( Exception ex2 ) {
167
 
                // no getter -- don't worry about it...
168
 
              }
169
 
            }
170
 
          }
171
 
        }
172
 
      }
173
 
    }
174
 
 
175
 
    private void storeName(Field annotation) {
176
 
      if (annotation.value().equals(Field.DEFAULT)) {
177
 
        if (field != null) {
178
 
          name = field.getName();
179
 
        } else {
180
 
          String setterName = setter.getName();
181
 
          if (setterName.startsWith("set") && setterName.length() > 3) {
182
 
            name = setterName.substring(3, 4).toLowerCase(Locale.ENGLISH) + setterName.substring(4);
183
 
          } else {
184
 
            name = setter.getName();
185
 
          }
186
 
        }
187
 
      }
188
 
      //dynamic fields are annotated as @Field("categories_*")
189
 
      else if(annotation.value().indexOf('*') >= 0){
190
 
        //if the field was annotated as a dynamic field, convert the name into a pattern
191
 
        //the wildcard (*) is supposed to be either a prefix or a suffix, hence the use of replaceFirst
192
 
        name = annotation.value().replaceFirst("\\*", "\\.*");
193
 
        dynamicFieldNamePatternMatcher = Pattern.compile("^"+name+"$");
194
 
      } else {
195
 
        name = annotation.value();
196
 
      }
197
 
    }
198
 
 
199
 
    private void storeType() {
200
 
      if (field != null) {
201
 
        type = field.getType();
202
 
      } else {
203
 
        Class[] params = setter.getParameterTypes();
204
 
        if (params.length != 1)
205
 
          throw new RuntimeException("Invalid setter method. Must have one and only one parameter");
206
 
        type = params[0];
207
 
      }
208
 
      if(type == Collection.class || type == List.class || type == ArrayList.class) {
209
 
        type = Object.class;
210
 
        isList = true;
211
 
        /*ParameterizedType parameterizedType = null;
212
 
        if(field !=null){
213
 
          if( field.getGenericType() instanceof ParameterizedType){
214
 
            parameterizedType = (ParameterizedType) field.getGenericType();
215
 
            Type[] types = parameterizedType.getActualTypeArguments();
216
 
            if (types != null && types.length > 0) type = (Class) types[0];
217
 
          }
218
 
        }*/
219
 
      } else if(type == byte[].class){
220
 
        //no op
221
 
      }else if (type.isArray()) {
222
 
        isArray = true;
223
 
        type = type.getComponentType();
224
 
      }
225
 
      //corresponding to the support for dynamicFields
226
 
      else if (type == Map.class || type == HashMap.class) {
227
 
        isContainedInMap = true;
228
 
        //assigned a default type
229
 
        type = Object.class;
230
 
        if(field != null){
231
 
          if(field.getGenericType() instanceof ParameterizedType){
232
 
            //check what are the generic values
233
 
            ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
234
 
            Type[] types = parameterizedType.getActualTypeArguments();
235
 
            if(types != null && types.length == 2 && types[0] == String.class){
236
 
              //the key should always be String
237
 
              //Raw and primitive types
238
 
              if(types[1] instanceof Class){
239
 
                //the value could be multivalued then it is a List ,Collection,ArrayList
240
 
                if(types[1]== Collection.class || types[1] == List.class || types[1] == ArrayList.class){
241
 
                  type = Object.class;
242
 
                  isList = true;
243
 
                }else{
244
 
                  //else assume it is a primitive and put in the source type itself
245
 
                  type = (Class) types[1];
246
 
                }
247
 
              }
248
 
              //Of all the Parameterized types, only List is supported
249
 
              else if(types[1] instanceof ParameterizedType){
250
 
                Type rawType = ((ParameterizedType)types[1]).getRawType();
251
 
                if(rawType== Collection.class || rawType == List.class || rawType == ArrayList.class){
252
 
                  type = Object.class;
253
 
                  isList = true;
254
 
                }
255
 
              }
256
 
              //Array types
257
 
              else if(types[1] instanceof GenericArrayType){
258
 
                type = (Class) ((GenericArrayType) types[1]).getGenericComponentType();
259
 
                isArray = true;
260
 
              }
261
 
              //Throw an Exception if types are not known
262
 
              else{
263
 
                throw new RuntimeException("Allowed type for values of mapping a dynamicField are : " +
264
 
                    "Object, Object[] and List");
265
 
              }
266
 
            }
267
 
          }
268
 
        }
269
 
      }
270
 
    }
271
 
 
272
 
    /**
273
 
     * Called by the {@link #inject} method to read the value(s) for a field
274
 
     * This method supports reading of all "matching" fieldName's in the <code>SolrDocument</code>
275
 
     *
276
 
     * Returns <code>SolrDocument.getFieldValue</code> for regular fields,
277
 
     * and <code>Map<String, List<Object>></code> for a dynamic field. The key is all matching fieldName's.
278
 
     */
279
 
    @SuppressWarnings("unchecked")
280
 
    private Object getFieldValue(SolrDocument sdoc){
281
 
      Object fieldValue = sdoc.getFieldValue(name);
282
 
      if(fieldValue != null) {
283
 
        //this is not a dynamic field. so return te value
284
 
        return fieldValue;
285
 
      }
286
 
      //reading dynamic field values
287
 
      if(dynamicFieldNamePatternMatcher != null){
288
 
        Map<String, Object> allValuesMap = null;
289
 
        ArrayList allValuesList = null;
290
 
        if(isContainedInMap){
291
 
         allValuesMap = new HashMap<String, Object>();
292
 
        } else {
293
 
          allValuesList = new ArrayList();
294
 
        }
295
 
        for(String field : sdoc.getFieldNames()){
296
 
          if(dynamicFieldNamePatternMatcher.matcher(field).find()){
297
 
            Object val = sdoc.getFieldValue(field);
298
 
            if(val == null) continue;
299
 
            if(isContainedInMap){
300
 
              if(isList){
301
 
                if (!(val instanceof List)) {
302
 
                  ArrayList al = new ArrayList();
303
 
                  al.add(val);
304
 
                  val = al;
305
 
                }
306
 
              } else if(isArray){
307
 
                if (!(val instanceof List)) {
308
 
                  Object[] arr= (Object[]) Array.newInstance(type,1);
309
 
                  arr[0] = val;
310
 
                  val= arr;
311
 
                } else {
312
 
                  val = Array.newInstance(type,((List)val).size());
313
 
                }
314
 
              }
315
 
              allValuesMap.put(field, val);
316
 
            }else {
317
 
              if (val instanceof Collection) {
318
 
                allValuesList.addAll((Collection) val);
319
 
              } else {
320
 
                allValuesList.add(val);
321
 
              }
322
 
            }
323
 
          }
324
 
        }
325
 
        if (isContainedInMap) {
326
 
          return allValuesMap.isEmpty() ? null : allValuesMap;
327
 
        } else {
328
 
          return allValuesList.isEmpty() ? null : allValuesList;
329
 
        }
330
 
      }
331
 
      return null;
332
 
    }
333
 
    <T> void inject(T obj, SolrDocument sdoc) {
334
 
      Object val = getFieldValue(sdoc);
335
 
      if(val == null) {
336
 
        return;
337
 
      }
338
 
      if(isArray && !isContainedInMap){
339
 
        List list = null;
340
 
        if(val.getClass().isArray()){
341
 
          set(obj,val);
342
 
          return;
343
 
        } else if (val instanceof List) {
344
 
          list = (List) val;
345
 
        } else{
346
 
          list = new ArrayList();
347
 
          list.add(val);
348
 
        }
349
 
        set(obj, list.toArray((Object[]) Array.newInstance(type,list.size())));        
350
 
      } else if(isList && !isContainedInMap){
351
 
        if (!(val instanceof List)) {
352
 
          ArrayList list = new ArrayList();
353
 
          list.add(val);
354
 
          val =  list;
355
 
        }
356
 
        set(obj, val);
357
 
      } else if(isContainedInMap){
358
 
        if (val instanceof Map) {
359
 
          set(obj,  val);
360
 
        }
361
 
      } else {
362
 
        set(obj, val);
363
 
      }
364
 
 
365
 
    }
366
 
 
367
 
 
368
 
    private void set(Object obj, Object v) {
369
 
      if(v!= null && type == ByteBuffer.class && v.getClass()== byte[].class) {
370
 
        v = ByteBuffer.wrap((byte[])v);
371
 
      }
372
 
      try {
373
 
        if (field != null) {
374
 
          field.set(obj, v);
375
 
        } else if (setter != null) {
376
 
          setter.invoke(obj, v);
377
 
        }
378
 
      } 
379
 
      catch (Exception e) {
380
 
        throw new RuntimeException("Exception while setting value : "+v+" on " + (field != null ? field : setter), e);
381
 
      }
382
 
    }
383
 
    
384
 
    public Object get( final Object obj )
385
 
    {
386
 
      if (field != null) {
387
 
        try {
388
 
          return field.get(obj);
389
 
        } 
390
 
        catch (Exception e) {        
391
 
          throw new RuntimeException("Exception while getting value: " + field, e);
392
 
        }
393
 
      }
394
 
      else if (getter == null) {
395
 
        throw new RuntimeException( "Missing getter for field: "+name+" -- You can only call the 'get' for fields that have a field of 'get' method" );
396
 
      }
397
 
      
398
 
      try {
399
 
        return getter.invoke( obj, (Object[])null );
400
 
      } 
401
 
      catch (Exception e) {        
402
 
        throw new RuntimeException("Exception while getting value: " + getter, e);
403
 
      }
404
 
    }
405
 
  }
406
 
}