001/* 002 * Copyright 2001-2013 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.joda.beans.query; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.List; 021 022import org.joda.beans.Bean; 023import org.joda.beans.BeanQuery; 024import org.joda.beans.MetaProperty; 025 026/** 027 * A chained query, that allows two or more queries to be joined. 028 * <p> 029 * For example, consider a structure where class A has a property b of type B, 030 * and class B has a property c of type C. The compound query allows property 031 * c to be accessed directly from an instance of A. 032 * 033 * @param <P> the type of the result of the query 034 * @author Stephen Colebourne 035 */ 036public final class ChainedBeanQuery<P> implements BeanQuery<P> { 037 038 /** 039 * The list of queries. 040 */ 041 private final List<BeanQuery<? extends Bean>> chain; 042 /** 043 * The last query. 044 */ 045 private final BeanQuery<P> last; 046 047 /** 048 * Obtains a chained query from two other queries. 049 * <p> 050 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters 051 * are in fact meta-properties. 052 * 053 * @param <P> the result type 054 * @param prop1 the first query, not null 055 * @param prop2 the second query, not null 056 * @return the compound query, not null 057 * @throws IllegalArgumentException if unable to obtain the meta-bean 058 */ 059 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<P> prop2) { 060 if (prop1 == null || prop2 == null) { 061 throw new NullPointerException("BeanQuery must not be null"); 062 } 063 List<BeanQuery<? extends Bean>> list = Collections.<BeanQuery<? extends Bean>>singletonList(prop1); 064 return new ChainedBeanQuery<P>(list, prop2); 065 } 066 067 /** 068 * Obtains a chained query from three queries. 069 * <p> 070 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters 071 * are in fact meta-properties. 072 * 073 * @param <P> the result type 074 * @param prop1 the first query, not null 075 * @param prop2 the second query, not null 076 * @param prop3 the third query, not null 077 * @return the compound query, not null 078 * @throws IllegalArgumentException if unable to obtain the meta-bean 079 */ 080 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<? extends Bean> prop2, BeanQuery<P> prop3) { 081 if (prop1 == null || prop2 == null || prop3 == null) { 082 throw new NullPointerException("BeanQuery must not be null"); 083 } 084 List<BeanQuery<? extends Bean>> list = new ArrayList<BeanQuery<? extends Bean>>(); 085 list.add(prop1); 086 list.add(prop2); 087 return new ChainedBeanQuery<P>(list, prop3); 088 } 089 090 /** 091 * Obtains a chained query from four queries. 092 * <p> 093 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters 094 * are in fact meta-properties. 095 * 096 * @param <P> the result type 097 * @param prop1 the first query, not null 098 * @param prop2 the second query, not null 099 * @param prop3 the third query, not null 100 * @param prop4 the fourth query, not null 101 * @return the compound query, not null 102 * @throws IllegalArgumentException if unable to obtain the meta-bean 103 */ 104 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<? extends Bean> prop2, BeanQuery<? extends Bean> prop3, BeanQuery<P> prop4) { 105 if (prop1 == null || prop2 == null || prop3 == null || prop4 == null) { 106 throw new NullPointerException("BeanQuery must not be null"); 107 } 108 List<BeanQuery<? extends Bean>> list = new ArrayList<BeanQuery<? extends Bean>>(); 109 list.add(prop1); 110 list.add(prop2); 111 list.add(prop3); 112 return new ChainedBeanQuery<P>(list, prop4); 113 } 114 115 //------------------------------------------------------------------------- 116 /** 117 * Restricted constructor. 118 */ 119 private ChainedBeanQuery(List<BeanQuery<? extends Bean>> metaProperties, BeanQuery<P> last) { 120 this.chain = metaProperties; 121 this.last = last; 122 } 123 124 //----------------------------------------------------------------------- 125 /** 126 * Gets the list of queries being chained. 127 * <p> 128 * {@link MetaProperty} implements {@link BeanQuery}, so typically the chain 129 * is formed from meta-properties. 130 * 131 * @return the list of all meta-properties being chained, not null 132 */ 133 public List<BeanQuery<?>> getChain() { 134 List<BeanQuery<?>> list = new ArrayList<BeanQuery<?>>(chain); 135 list.add(last); 136 return list; 137 } 138 139 //------------------------------------------------------------------------- 140 @Override 141 public P get(Bean bean) { 142 for (BeanQuery<? extends Bean> mp : chain) { 143 bean = mp.get(bean); 144 } 145 return last.get(bean); 146 } 147 148 //------------------------------------------------------------------------- 149 @Override 150 public String toString() { 151 StringBuilder buf = new StringBuilder(64); 152 for (BeanQuery<? extends Bean> mp : chain) { 153 buf.append(mp).append('.'); 154 } 155 buf.append(last); 156 return buf.toString(); 157 } 158 159}