Source code: org/objectstyle/cayenne/map/Entity.java
1 /* ====================================================================
2 *
3 * The ObjectStyle Group Software License, Version 1.0
4 *
5 * Copyright (c) 2002-2003 The ObjectStyle Group
6 * and individual authors of the software. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution, if
21 * any, must include the following acknowlegement:
22 * "This product includes software developed by the
23 * ObjectStyle Group (http://objectstyle.org/)."
24 * Alternately, this acknowlegement may appear in the software itself,
25 * if and wherever such third-party acknowlegements normally appear.
26 *
27 * 4. The names "ObjectStyle Group" and "Cayenne"
28 * must not be used to endorse or promote products derived
29 * from this software without prior written permission. For written
30 * permission, please contact andrus@objectstyle.org.
31 *
32 * 5. Products derived from this software may not be called "ObjectStyle"
33 * nor may "ObjectStyle" appear in their names without prior written
34 * permission of the ObjectStyle Group.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 * ====================================================================
49 *
50 * This software consists of voluntary contributions made by many
51 * individuals on behalf of the ObjectStyle Group. For more
52 * information on the ObjectStyle Group, please see
53 * <http://objectstyle.org/>.
54 *
55 */
56 package org.objectstyle.cayenne.map;
57
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.Iterator;
61 import java.util.Map;
62 import java.util.StringTokenizer;
63
64 import org.objectstyle.cayenne.exp.Expression;
65 import org.objectstyle.cayenne.exp.ExpressionException;
66 import org.objectstyle.cayenne.query.Query;
67 import org.objectstyle.cayenne.query.SelectQuery;
68 import org.objectstyle.cayenne.util.CayenneMap;
69
70 /**
71 * An Entity is an abstract descriptor for an entity mapping concept.
72 * Entity can represent either a descriptor of database table or
73 * a persistent object.
74 *
75 * @author Andrei Adamchik
76 */
77 public abstract class Entity extends MapObject {
78 public static final String PATH_SEPARATOR = ".";
79
80 protected CayenneMap attributes = new CayenneMap(this);
81 protected Map attributesMapRef = Collections.unmodifiableMap(attributes);
82 protected Collection attributesRef = Collections.unmodifiableCollection(attributes.values());
83 protected CayenneMap relationships = new CayenneMap(this);
84 protected Collection relationshipsRef = Collections.unmodifiableCollection(relationships.values());
85 protected CayenneMap queries = new CayenneMap(this);
86
87 /**
88 * @return parent DataMap of this entity.
89 */
90 public DataMap getDataMap() {
91 return (DataMap) getParent();
92 }
93
94 /**
95 * Sets parent DataMap of this entity.
96 */
97 public void setDataMap(DataMap dataMap) {
98 this.setParent(dataMap);
99 }
100
101 /**
102 * Returns a named query associated with this entity.
103 */
104 public SelectQuery getQuery(String queryName) {
105 return (SelectQuery) queries.get(queryName);
106 }
107
108 /**
109 * Creates a named association of a SelectQuery with this entity. Throws
110 * IllegalArgumentException if query root can not be resolved to this
111 * entity.
112 */
113 public void addQuery(String queryName, SelectQuery query) {
114 if (query == null) {
115 throw new IllegalArgumentException("Attempt to add null query.");
116 }
117
118 if (queryName == null) {
119 throw new IllegalArgumentException("Attempt to add query with null name.");
120 }
121
122 // check if this is the right query
123 this.validateQueryRoot(query);
124 queries.put(queryName, query);
125 }
126
127 /**
128 * Removes a named query from this Entity.
129 */
130 public void removeQuery(String queryName) {
131 queries.remove(queryName);
132 }
133
134 public void clearQueries() {
135 queries.clear();
136 }
137
138 /**
139 * Helper method that checks that a Query belongs to this entity by
140 * validating query root object.
141 *
142 * @throws IllegalArgumentException if query does not belong to this entity.
143 */
144 protected abstract void validateQueryRoot(Query query)
145 throws IllegalArgumentException;
146
147 /**
148 * Returns attribute with name <code>attrName</code>.
149 * Will return null if no attribute with this name exists.
150 */
151 public Attribute getAttribute(String attrName) {
152 return (Attribute) attributes.get(attrName);
153 }
154
155 /**
156 * Adds new attribute to the entity. If attribute has no name,
157 * IllegalArgumentException is thrown.
158 *
159 * Also sets <code>attr</code>'s entity to be this entity.
160 */
161 public void addAttribute(Attribute attr) {
162 if (attr.getName() == null) {
163 throw new IllegalArgumentException("Attempt to insert unnamed attribute.");
164 }
165
166 attributes.put(attr.getName(), attr);
167 }
168
169 /** Removes an attribute named <code>attrName</code>.*/
170 public void removeAttribute(String attrName) {
171 attributes.remove(attrName);
172 }
173
174 public void clearAttributes() {
175 attributes.clear();
176 }
177
178 /**
179 * Returns relationship with name <code>relName</code>.
180 * Will return null if no relationship with this name
181 * exists in the entity.
182 */
183 public Relationship getRelationship(String relName) {
184 return (Relationship) relationships.get(relName);
185 }
186
187 /** Adds new relationship to the entity. */
188 public void addRelationship(Relationship rel) {
189 relationships.put(rel.getName(), rel);
190 }
191
192 /** Removes a relationship named <code>attrName</code>.*/
193 public void removeRelationship(String relName) {
194 relationships.remove(relName);
195 }
196
197 public void clearRelationships() {
198 relationships.clear();
199 }
200
201 public Map getRelationshipMap() {
202 return Collections.unmodifiableMap(relationships);
203 }
204
205 /**
206 * Returns a collection of Relationships that exist in this entity.
207 */
208 public Collection getRelationships() {
209 return relationshipsRef;
210 }
211
212 /**
213 * Returns entity attributes as an unmodifiable map.
214 */
215 public Map getAttributeMap() {
216 return attributesMapRef;
217 }
218
219 /**
220 * Returns entity attributes.
221 */
222 public Collection getAttributes() {
223 return attributesRef;
224 }
225
226 /**
227 * Processes expression <code>objPathExp</code> and returns an Iterator
228 * of path components that contains a sequence of Attributes and Relationships.
229 * Note that if path is invalid and can not be resolved from this entity,
230 * this method will still return an Iterator, but an attempt to read the first
231 * invalid path component will result in ExpressionException.
232 *
233 * @see org.objectstyle.cayenne.exp.Expression#OBJ_PATH for definition of OBJ_PATH.
234 *
235 * @throws org.objectstyle.cayenne.exp.ExpressionException Exception is thrown if
236 * <code>objPathExp</code> is not of type OBJ_PATH
237 */
238 public Iterator resolvePathComponents(Expression pathExp)
239 throws ExpressionException {
240 if (pathExp.getType() != Expression.OBJ_PATH
241 && pathExp.getType() != Expression.DB_PATH) {
242 StringBuffer msg = new StringBuffer();
243 msg
244 .append("Invalid expression type: '")
245 .append(pathExp.getType())
246 .append("' ('")
247 .append(Expression.OBJ_PATH)
248 .append(" or ")
249 .append(Expression.DB_PATH)
250 .append("' is expected).");
251
252 throw new ExpressionException(msg.toString());
253 }
254
255 return new PathIterator(this, (String) pathExp.getOperand(0));
256 }
257
258 public Iterator resolvePathComponents(String path)
259 throws ExpressionException {
260 return new PathIterator(this, path);
261 }
262
263 // Used to return an iterator to callers of 'resolvePathComponents'
264 protected final class PathIterator implements Iterator {
265 private StringTokenizer toks;
266 private Entity currentEnt;
267
268 PathIterator(Entity ent, String path) {
269 super();
270 this.currentEnt = ent;
271 this.toks = new StringTokenizer(path, PATH_SEPARATOR);
272 }
273
274 public boolean hasNext() {
275 return toks.hasMoreTokens();
276 }
277
278 public Object next() {
279 String pathComp = toks.nextToken();
280
281 // see if this is an attribute
282 Attribute attr = currentEnt.getAttribute(pathComp);
283 if (attr != null) {
284 // do a sanity check...
285 if (toks.hasMoreTokens()) {
286 throw new ExpressionException(
287 "Attribute must be the last component of the path: '"
288 + pathComp
289 + "'.");
290 }
291
292 return attr;
293 }
294
295 Relationship rel = currentEnt.getRelationship(pathComp);
296 if (rel != null) {
297 currentEnt = rel.getTargetEntity();
298 return rel;
299 }
300
301 // build error message
302 StringBuffer buf = new StringBuffer();
303 buf
304 .append("Can't resolve path component: [")
305 .append(currentEnt.getName())
306 .append('.')
307 .append(pathComp)
308 .append("].");
309 throw new ExpressionException(buf.toString());
310 }
311
312 public void remove() {
313 throw new UnsupportedOperationException("'remove' operation is not supported.");
314 }
315 }
316 }