1 /*
2 * Copyright 2001-2013 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.beans.query;
17
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.List;
21
22 import org.joda.beans.Bean;
23 import org.joda.beans.BeanQuery;
24 import org.joda.beans.MetaProperty;
25
26 /**
27 * A chained query, that allows two or more queries to be joined.
28 * <p>
29 * For example, consider a structure where class A has a property b of type B,
30 * and class B has a property c of type C. The compound query allows property
31 * c to be accessed directly from an instance of A.
32 *
33 * @param <P> the type of the result of the query
34 * @author Stephen Colebourne
35 */
36 public final class ChainedBeanQuery<P> implements BeanQuery<P> {
37
38 /**
39 * The list of queries.
40 */
41 private final List<BeanQuery<? extends Bean>> chain;
42 /**
43 * The last query.
44 */
45 private final BeanQuery<P> last;
46
47 /**
48 * Obtains a chained query from two other queries.
49 * <p>
50 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters
51 * are in fact meta-properties.
52 *
53 * @param <P> the result type
54 * @param prop1 the first query, not null
55 * @param prop2 the second query, not null
56 * @return the compound query, not null
57 * @throws IllegalArgumentException if unable to obtain the meta-bean
58 */
59 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<P> prop2) {
60 if (prop1 == null || prop2 == null) {
61 throw new NullPointerException("BeanQuery must not be null");
62 }
63 List<BeanQuery<? extends Bean>> list = Collections.<BeanQuery<? extends Bean>>singletonList(prop1);
64 return new ChainedBeanQuery<P>(list, prop2);
65 }
66
67 /**
68 * Obtains a chained query from three queries.
69 * <p>
70 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters
71 * are in fact meta-properties.
72 *
73 * @param <P> the result type
74 * @param prop1 the first query, not null
75 * @param prop2 the second query, not null
76 * @param prop3 the third query, not null
77 * @return the compound query, not null
78 * @throws IllegalArgumentException if unable to obtain the meta-bean
79 */
80 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<? extends Bean> prop2, BeanQuery<P> prop3) {
81 if (prop1 == null || prop2 == null || prop3 == null) {
82 throw new NullPointerException("BeanQuery must not be null");
83 }
84 List<BeanQuery<? extends Bean>> list = new ArrayList<BeanQuery<? extends Bean>>();
85 list.add(prop1);
86 list.add(prop2);
87 return new ChainedBeanQuery<P>(list, prop3);
88 }
89
90 /**
91 * Obtains a chained query from four queries.
92 * <p>
93 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters
94 * are in fact meta-properties.
95 *
96 * @param <P> the result type
97 * @param prop1 the first query, not null
98 * @param prop2 the second query, not null
99 * @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 }