2017-05-26 9 views
0

私はしばしば他のDTOを含むDTOを扱わなければならず、あるオブジェクトの属性(およびそれら自身の属性、再帰的に)をスキャンし、クラスBingo全体の階層。例えばネストされたオブジェクトの特定の型の属性を見つける

、私は次がある場合:

public static class Bingo { 
    // the one I want to get 
} 

public static class Foo { 
    private Bar bar; 
    private Bingo bingo; 
    private List<Bingo> bingos; 

    // getters & setters 
} 

public static class Bar { 

    private Bingo bingo; 

    // getters & setters 
} 

私はBarオブジェクト内のものとListを含め、私のFooオブジェクトの属性、で見つかったBingoのすべてのインスタンスを取得したいのですが。

便利なことに、ライブラリがありますか?

(のJUnitのビットを使用して)、より完全なテストケース:

public static class Bingo { 
    private final int id; 

    public Bingo(int in_id) { 
     id = in_id; 
    } 

    @Override 
    public String toString() { 
     return "Bingo#"+String.valueOf(id); 
    } 

} 

public static class BingoWrapper { 

    private Bingo bingo; 

    public Bingo getBingo() { 
     return bingo; 
    } 

    public void setBingo(Bingo in_bingo) { 
     bingo = in_bingo; 
    } 
} 

public static class BingoFactory { 

    private final List<Bingo> ALL_BINGOS = new ArrayList<>(); 
    private int sequence = 0; 

    public Bingo createBingo(){ 
     Bingo l_bingo = new Bingo(sequence++); 
     ALL_BINGOS.add(l_bingo); 
     return l_bingo; 
    } 

    public BingoWrapper createBingoWrapper(){ 
     BingoWrapper l_bar = new BingoWrapper(); 
     l_bar.setBingo(createBingo()); 
     return l_bar; 
    } 

    public List<Bingo> getAllBingos(){ 
     return ALL_BINGOS.stream().collect(Collectors.toList()); 
    } 

} 

public static class Foo { 

    private Bingo bingo; 
    private BingoWrapper wrapper; 
    private Bingo[] array; 
    private Collection<Object> collection; 
    private Map<Object,Object> map; 

    public Bingo getBingo() { 
     return bingo; 
    } 
    public void setBingo(Bingo in_bingo) { 
     bingo = in_bingo; 
    } 
    public BingoWrapper getWrapper() { 
     return wrapper; 
    } 
    public void setWrapper(BingoWrapper in_bar) { 
     wrapper = in_bar; 
    } 
    public Bingo[] getArray() { 
     return array; 
    } 
    public void setArray(Bingo[] in_array) { 
     array = in_array; 
    } 
    public Collection<Object> getCollection() { 
     return collection; 
    } 
    public void setCollection(Collection<Object> in_collection) { 
     collection = in_collection; 
    } 
    public Map<Object, Object> getMap() { 
     return map; 
    } 
    public void setMap(Map<Object, Object> in_map) { 
     map = in_map; 
    } 
} 

@Test 
public void test(){ 
    BingoFactory l_bingoFactory = new BingoFactory(); 

    Foo l_foo = new Foo(); 
    l_foo.setBingo(l_bingoFactory.createBingo());     // one in a field 
    l_foo.setWrapper(l_bingoFactory.createBingoWrapper());   // one in a field of a field 

    l_foo.setArray(new Bingo[]{l_bingoFactory.createBingo()});  // one in an array in a field 

    l_foo.setCollection(Arrays.asList(
      l_bingoFactory.createBingo(),       // one in Collection in a field 
      l_bingoFactory.createBingoWrapper()));     // one in a field of an item in a Collection in a field 

    Map<Object,Object> l_map = new HashMap<>(); 
    l_foo.setMap(l_map); 
    l_map.put("key", l_bingoFactory.createBingo());    // one as a key in a Map in a field 
    l_map.put(l_bingoFactory.createBingo(), "value");    // one as a value in a Map in a field 
    l_map.put("keyAgain", l_bingoFactory.createBingoWrapper()); // one wrapped in a value in a Map in a Field 
    l_map.put(l_bingoFactory.createBingoWrapper(), "valueAgain"); // one wrapped in a key in a Map in a field 

    List<Bingo> l_found = BeanUtils.scanObjectForType(l_foo, Bingo.class); // Magic happens here 

    System.out.println(l_found);         // for debug 
    Assert.assertTrue(l_found.containsAll(l_bingoFactory.getAllBingos())); // I want them ALL 
} 

答えて

0

溶液Spring's BeanUtils有する:(Iはスキャンか否するために必要な入力クラスのオブジェクトに対し決定するブール値を追加しました。 (つまり、あなたはタイプBingoの他のオブジェクトを含むためにあなたのBingoオブジェクトを期待していますか?))

public static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType){ 
    return scanObjectForType(in_object, in_type, in_scanSameType, new HashSet<>()); 
} 

private static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType, Set<Object> in_alreadyScanned){ 
    if(in_type == null){ 
     throw new IllegalArgumentException("in_type should not be null"); 
    } 
    if(in_object instanceof Class){ 
     throw new IllegalArgumentException("in_type should not be a Class"); 
    } 
    if(in_object == null || in_alreadyScanned.contains(in_object)){ 
     return Collections.emptyList(); 
    } 
    in_alreadyScanned.add(in_object); // to prevent infinite loop when inner object references outer object 

    if(in_type.isInstance(in_object)){ 
     return Collections.singletonList((T) in_object); 
    } 

    List<T> l_result = new ArrayList<>(); 
    if(in_type.isInstance(in_object)){ 
     l_result.add((T) in_object); 
     if(!in_scanSameType){ 
      return l_result; 
     } 
    } 
    if(in_object instanceof Object[]){ 
     for(Object l_item : (Object[]) in_object){ 
      l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned)); 
     } 
    } else if(in_object instanceof Collection){ 
     for(Object l_item : (Collection<Object>) in_object){ 
      l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned)); 
     } 
    } else if(in_object instanceof Map){ 
     Map<Object,Object> l_map = (Map<Object,Object>) in_object; 
     for(Map.Entry<Object, Object> l_entry : l_map.entrySet()){ 
      l_result.addAll(scanObjectForType(l_entry.getKey(), in_type, in_scanSameType, in_alreadyScanned));  
      l_result.addAll(scanObjectForType(l_entry.getValue(), in_type, in_scanSameType, in_alreadyScanned)); 
     } 
    } else { 
     PropertyDescriptor[] l_descriptors = org.springframework.beans.BeanUtils.getPropertyDescriptors(in_object.getClass()); 
     for(PropertyDescriptor l_descriptor : l_descriptors){ 
      Method l_readMethod = l_descriptor.getReadMethod(); 
      if(l_readMethod != null){ 
       try { 
        Object l_readObject = l_readMethod.invoke(in_object); 
        if(l_readObject != null 
          && !l_readObject.equals(in_object)  // prevents infinite loops 
          && !(l_readObject instanceof Class)){ // prevents weird loops when accessing properties of classes 
         l_result.addAll(scanObjectForType(l_readObject,in_type, in_scanSameType, in_alreadyScanned)); 
        } 
       } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 
        // too bad but continue 
        LOGGER.warn("Got an error trying to access field : ", e); 
        continue; 
       } 
      } 
     } 
    } 
    return l_result; 
} 

その限界:

  • パブリックアクセサーでスキャンプロパティのみ
  • Classタイプをスキャンしません(ClassLoaderのクラス全体のスキャンを防止し、ユースケースがDTO指向であるため)。
  • 再帰性に依存します。入れ子になった豆のSetのループで動作するBeanVisitorオブジェクトを実装する方がきれいかもしれないと思います。
  • スキャン対象プロパティではないゲッターメソッドによって返されるオブジェクト。
  • これは継承でテストされていません。
関連する問題