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 */
016 package org.joda.beans.query;
017
018 import java.util.ArrayList;
019 import java.util.Collections;
020 import java.util.List;
021
022 import org.joda.beans.Bean;
023 import org.joda.beans.BeanQuery;
024 import 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 */
036 public 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 prop1 the first query, not null
054 * @param prop2 the second query, not null
055 * @return the compound query, not null
056 * @throws IllegalArgumentException if unable to obtain the meta-bean
057 */
058 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<P> prop2) {
059 if (prop1 == null || prop2 == null) {
060 throw new NullPointerException("BeanQuery must not be null");
061 }
062 List<BeanQuery<? extends Bean>> list = Collections.<BeanQuery<? extends Bean>>singletonList(prop1);
063 return new ChainedBeanQuery<P>(list, prop2);
064 }
065
066 /**
067 * Obtains a chained query from three queries.
068 * <p>
069 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters
070 * are in fact meta-properties.
071 *
072 * @param prop1 the first query, not null
073 * @param prop2 the second query, not null
074 * @param prop3 the third query, not null
075 * @return the compound query, not null
076 * @throws IllegalArgumentException if unable to obtain the meta-bean
077 */
078 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<? extends Bean> prop2, BeanQuery<P> prop3) {
079 if (prop1 == null || prop2 == null || prop3 == null) {
080 throw new NullPointerException("BeanQuery must not be null");
081 }
082 List<BeanQuery<? extends Bean>> list = new ArrayList<BeanQuery<? extends Bean>>();
083 list.add(prop1);
084 list.add(prop2);
085 return new ChainedBeanQuery<P>(list, prop3);
086 }
087
088 /**
089 * Obtains a chained query from four queries.
090 * <p>
091 * {@link MetaProperty} implements {@link BeanQuery}, so typically the parameters
092 * are in fact meta-properties.
093 *
094 * @param prop1 the first query, not null
095 * @param prop2 the second query, not null
096 * @param prop3 the third query, not null
097 * @param prop4 the fourth query, not null
098 * @return the compound query, not null
099 * @throws IllegalArgumentException if unable to obtain the meta-bean
100 */
101 public static <P> ChainedBeanQuery<P> of(BeanQuery<? extends Bean> prop1, BeanQuery<? extends Bean> prop2, BeanQuery<? extends Bean> prop3, BeanQuery<P> prop4) {
102 if (prop1 == null || prop2 == null || prop3 == null || prop4 == null) {
103 throw new NullPointerException("BeanQuery must not be null");
104 }
105 List<BeanQuery<? extends Bean>> list = new ArrayList<BeanQuery<? extends Bean>>();
106 list.add(prop1);
107 list.add(prop2);
108 list.add(prop3);
109 return new ChainedBeanQuery<P>(list, prop4);
110 }
111
112 //-------------------------------------------------------------------------
113 /**
114 * Restricted constructor.
115 */
116 private ChainedBeanQuery(List<BeanQuery<? extends Bean>> metaProperties, BeanQuery<P> last) {
117 this.chain = metaProperties;
118 this.last = last;
119 }
120
121 //-----------------------------------------------------------------------
122 /**
123 * Gets the list of queries being chained.
124 * <p>
125 * {@link MetaProperty} implements {@link BeanQuery}, so typically the chain
126 * is formed from meta-properties.
127 *
128 * @return the list of all meta-properties being chained, not null
129 */
130 public List<BeanQuery<?>> getChain() {
131 List<BeanQuery<?>> list = new ArrayList<BeanQuery<?>>(chain);
132 list.add(last);
133 return list;
134 }
135
136 //-------------------------------------------------------------------------
137 @Override
138 public P get(Bean bean) {
139 for (BeanQuery<? extends Bean> mp : chain) {
140 bean = mp.get(bean);
141 }
142 return last.get(bean);
143 }
144
145 //-------------------------------------------------------------------------
146 @Override
147 public String toString() {
148 StringBuilder buf = new StringBuilder(64);
149 for (BeanQuery<? extends Bean> mp : chain) {
150 buf.append(mp).append('.');
151 }
152 buf.append(last);
153 return buf.toString();
154 }
155
156 }