Source code: org/objectstyle/cayenne/wocompat/EOModelProcessor.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.wocompat;
57
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Map;
63
64 import org.objectstyle.cayenne.CayenneRuntimeException;
65 import org.objectstyle.cayenne.dba.TypesMapping;
66 import org.objectstyle.cayenne.exp.Expression;
67 import org.objectstyle.cayenne.exp.ExpressionFactory;
68 import org.objectstyle.cayenne.map.DataMap;
69 import org.objectstyle.cayenne.map.DbAttribute;
70 import org.objectstyle.cayenne.map.DbAttributePair;
71 import org.objectstyle.cayenne.map.DbEntity;
72 import org.objectstyle.cayenne.map.DbRelationship;
73 import org.objectstyle.cayenne.map.ObjAttribute;
74 import org.objectstyle.cayenne.map.ObjEntity;
75 import org.objectstyle.cayenne.map.ObjRelationship;
76
77 /**
78 * Class that converts EOModels to org.objectstyle.cayenne.map.DataMap objects.
79 */
80 public class EOModelProcessor {
81
82 /** Performs EOModel loading.
83 *
84 * @param path A path to ".eomodeld" directory.
85 * If path doesn't end with ".eomodeld", ".eomodeld"
86 * suffix is automatically assumed. */
87 public DataMap loadEOModel(String path) throws Exception {
88 EOModelHelper helper = makeHelper(path);
89
90 // create empty map
91 DataMap dataMap = helper.getDataMap();
92
93 // process enitities
94 Iterator it = helper.modelNames();
95 while (it.hasNext()) {
96 String name = (String) it.next();
97
98 // create and register entity
99 ObjEntity e = makeEntity(helper, name);
100
101 // process entity attributes
102 makeAttributes(helper, e);
103 }
104
105 // after all entities are loaded,
106 // process relationships
107 it = helper.modelNames();
108 while (it.hasNext()) {
109 String name = (String) it.next();
110 makeRelationships(helper, dataMap.getObjEntity(name));
111 }
112
113 // after all normal relationships are loaded,
114 // process falttened relationships
115 it = helper.modelNames();
116 while (it.hasNext()) {
117 String name = (String) it.next();
118 makeFlatRelationships(helper, dataMap.getObjEntity(name));
119 }
120
121 return dataMap;
122 }
123
124 /**
125 * Creates an returns new EOModelHelper to process EOModel.
126 * Exists mostly for the benefit of subclasses.
127 */
128 protected EOModelHelper makeHelper(String path) throws Exception {
129 return new EOModelHelper(path);
130 }
131
132 /**
133 * Creates and returns a new ObjEntity linked to a corresponding DbEntity.
134 */
135 protected ObjEntity makeEntity(EOModelHelper helper, String name) {
136 DataMap dataMap = helper.getDataMap();
137
138 // create ObjEntity
139 ObjEntity e = new ObjEntity(name);
140 e.setClassName(helper.entityClass(name));
141
142 // create DbEntity...since EOF allows the same table to be
143 // associated with multiple EOEntities, check for name duplicates
144 String dbEntityName = (String) helper.entityInfo(name).get("externalName");
145
146 if (dbEntityName != null) {
147 int i = 0;
148 String dbEntityBaseName = dbEntityName;
149 while (dataMap.getDbEntity(dbEntityName, false) != null) {
150 dbEntityName = dbEntityBaseName + i++;
151 }
152
153 DbEntity de = new DbEntity(dbEntityName);
154 dataMap.addDbEntity(de);
155 e.setDbEntity(de);
156 }
157
158 dataMap.addObjEntity(e);
159
160 return e;
161 }
162
163 /**
164 * Create ObjAttributes of the specified entity, as well as
165 * DbAttributes of the corresponding DbEntity.
166 */
167 protected void makeAttributes(EOModelHelper helper, ObjEntity e) {
168 Map entityMap = helper.entityInfo(e.getName());
169 List pks = (List) entityMap.get("primaryKeyAttributes");
170 List classProps = (List) entityMap.get("classProperties");
171 List attributes = (List) entityMap.get("attributes");
172 DbEntity dbEntity = e.getDbEntity();
173
174 String isReadOnlyString = (String) entityMap.get("isReadOnly");
175 e.setReadOnly("Y".equals(isReadOnlyString));
176
177 if (pks == null) {
178 pks = Collections.EMPTY_LIST;
179 }
180
181 if (classProps == null) {
182 classProps = Collections.EMPTY_LIST;
183 }
184
185 if (attributes == null) {
186 attributes = Collections.EMPTY_LIST;
187 }
188
189 // process attribute list creating both Db and Obj attributes
190
191 if (attributes == null) {
192 return;
193 }
194
195 Iterator it = attributes.iterator();
196 while (it.hasNext()) {
197 Map attrMap = (Map) it.next();
198
199 String prototypeName = (String) attrMap.get("prototypeName");
200 Map prototypeAttrMap = helper.getPrototypeAttributeMapFor(prototypeName);
201
202 String dbAttrName = (String) attrMap.get("columnName");
203 if (null == dbAttrName) dbAttrName = (String) prototypeAttrMap.get("columnName");;
204
205 String attrName = (String) attrMap.get("name");
206 if (null == attrName) attrName = (String) prototypeAttrMap.get("name");;
207
208 String attrType = (String) attrMap.get("valueClassName");
209 if (null == attrType) attrType = (String) prototypeAttrMap.get("valueClassName");;
210
211 String javaType = helper.javaTypeForEOModelerType(attrType);
212 EODbAttribute dbAttr = null;
213
214 if (dbAttrName != null && dbEntity != null) {
215
216 // create DbAttribute...since EOF allows the same column name for
217 // more than one Java attribute, we need to check for name duplicates
218 int i = 0;
219 String dbAttributeBaseName = dbAttrName;
220 while (dbEntity.getAttribute(dbAttrName) != null) {
221 dbAttrName = dbAttributeBaseName + i++;
222 }
223
224 dbAttr =
225 new EODbAttribute(
226 dbAttrName,
227 TypesMapping.getSqlTypeByJava(javaType),
228 dbEntity);
229 dbAttr.setEoAttributeName(attrName);
230 dbEntity.addAttribute(dbAttr);
231
232 Integer width = (Integer) attrMap.get("width");
233 if (null == width) width = (Integer) prototypeAttrMap.get("width");
234
235 if (width != null)
236 dbAttr.setMaxLength(width.intValue());
237
238 Integer scale = (Integer) attrMap.get("scale");
239 if (null == scale) scale = (Integer) prototypeAttrMap.get("scale");
240
241 if (scale != null)
242 dbAttr.setPrecision(scale.intValue());
243
244 if (pks.contains(attrName))
245 dbAttr.setPrimaryKey(true);
246
247 Object allowsNull = attrMap.get("allowsNull");
248 // TODO: Unclear that allowsNull should be inherited from EOPrototypes
249 // if (null == allowsNull) allowsNull = prototypeAttrMap.get("allowsNull");;
250
251 dbAttr.setMandatory(!"Y".equals(allowsNull));
252 }
253
254 if (classProps.contains(attrName)) {
255 ObjAttribute attr = new ObjAttribute(attrName, javaType, e);
256 attr.setDbAttribute(dbAttr);
257 e.addAttribute(attr);
258 }
259 }
260 }
261
262 /**
263 * Create ObjRelationships of the specified entity, as well as
264 * DbRelationships of the corresponding DbEntity.
265 */
266 protected void makeRelationships(EOModelHelper helper, ObjEntity e) {
267 Map info = helper.entityInfo(e.getName());
268 List classProps = (List) info.get("classProperties");
269 List rinfo = (List) info.get("relationships");
270
271 if (rinfo == null) {
272 return;
273 }
274
275 if (classProps == null) {
276 classProps = Collections.EMPTY_LIST;
277 }
278
279 DbEntity dbSrc = e.getDbEntity();
280 Iterator it = rinfo.iterator();
281 while (it.hasNext()) {
282 Map relMap = (Map) it.next();
283 String targetName = (String) relMap.get("destination");
284
285 // ignore flattened relationships for now
286 if (targetName == null) {
287 continue;
288 }
289
290 String relName = (String) relMap.get("name");
291 boolean toMany = "Y".equals(relMap.get("isToMany"));
292 boolean toDependentPK = "Y".equals(relMap.get("propagatesPrimaryKey"));
293 ObjEntity target = helper.getDataMap().getObjEntity(targetName);
294
295 if (target == null) {
296 continue;
297 }
298
299 DbEntity dbTarget = target.getDbEntity();
300 DbRelationship dbRel = null;
301 // process underlying DbRelationship
302 // Note - there is no flattened rel. support here....
303 if (dbTarget != null) {
304 dbRel = new DbRelationship();
305 dbRel.setSourceEntity(dbSrc);
306 dbRel.setTargetEntity(dbTarget);
307 dbRel.setToMany(toMany);
308 dbRel.setName(relName);
309 dbRel.setToDependentPK(toDependentPK);
310 dbSrc.addRelationship(dbRel);
311
312 List joins = (List) relMap.get("joins");
313 Iterator jIt = joins.iterator();
314 while (jIt.hasNext()) {
315 Map joinMap = (Map) jIt.next();
316 String srcAttrName = (String) joinMap.get("sourceAttribute");
317 String targetAttrName = (String) joinMap.get("destinationAttribute");
318
319 DbAttribute srcAttr =
320 EODbAttribute.findForEOAttributeName(dbSrc, srcAttrName);
321 DbAttribute targetAttr =
322 EODbAttribute.findForEOAttributeName(dbTarget, targetAttrName);
323
324 DbAttributePair join = new DbAttributePair(srcAttr, targetAttr);
325 dbRel.addJoin(join);
326 }
327 }
328
329 // only create obj relationship if it is a class property
330
331 if (classProps.contains(relName)) {
332 ObjRelationship rel = new ObjRelationship();
333 rel.setName(relName);
334 rel.setSourceEntity(e);
335 rel.setTargetEntity(target);
336 e.addRelationship(rel);
337
338 if (dbRel != null) {
339 rel.addDbRelationship(dbRel);
340 }
341 }
342 }
343 }
344
345 /**
346 * Create Flattened ObjRelationships of the specified entity.
347 */
348 protected void makeFlatRelationships(EOModelHelper helper, ObjEntity e) {
349 Map info = helper.entityInfo(e.getName());
350 List rinfo = (List) info.get("relationships");
351 if (rinfo == null) {
352 return;
353 }
354
355 Iterator it = rinfo.iterator();
356 while (it.hasNext()) {
357 Map relMap = (Map) it.next();
358 String targetPath = (String) relMap.get("definition");
359
360 // ignore normal relationships
361 if (targetPath == null) {
362 continue;
363 }
364
365 Expression exp = ExpressionFactory.unaryExp(Expression.DB_PATH, targetPath);
366 Iterator path = e.getDbEntity().resolvePathComponents(exp);
367
368 ObjRelationship flatRel = new ObjRelationship();
369 flatRel.setName((String) relMap.get("name"));
370
371 DbRelationship firstRel = null;
372 DbRelationship lastRel = null;
373 while (path.hasNext()) {
374 lastRel = (DbRelationship) path.next();
375 flatRel.addDbRelationship(lastRel);
376
377 if (firstRel == null) {
378 firstRel = lastRel;
379 }
380 }
381
382 if ((firstRel != null) && (lastRel != null)) {
383 flatRel.setSourceEntity(e);
384
385 Collection potentialTargets =
386 e.getDataMap().getMappedEntities(
387 (DbEntity) lastRel.getTargetEntity());
388
389 // sanity check
390 if (potentialTargets.size() != 1) {
391 throw new CayenneRuntimeException(
392 "One and only one entity should be mapped"
393 + " to "
394 + lastRel.getTargetEntity().getName()
395 + ". Instead found : "
396 + potentialTargets.size());
397 }
398
399 flatRel.setTargetEntity((ObjEntity) potentialTargets.iterator().next());
400 e.addRelationship(flatRel);
401 }
402 else {
403 throw new CayenneRuntimeException("relationship in path was null!");
404 }
405 }
406 }
407
408 /**
409 * Special DbAttribute subclass that stores extra info needed to work
410 * with EOModels.
411 */
412 static class EODbAttribute extends DbAttribute {
413 protected String eoAttributeName;
414
415 public static DbAttribute findForEOAttributeName(DbEntity e, String name) {
416 Iterator it = e.getAttributes().iterator();
417 while (it.hasNext()) {
418 EODbAttribute attr = (EODbAttribute) it.next();
419 if (name.equals(attr.getEoAttributeName())) {
420 return attr;
421 }
422 }
423 return null;
424 }
425
426 public EODbAttribute() {
427 }
428
429 public EODbAttribute(String name, int type, DbEntity entity) {
430 super(name, type, entity);
431 }
432
433 public String getEoAttributeName() {
434 return eoAttributeName;
435 }
436
437 public void setEoAttributeName(String eoAttributeName) {
438 this.eoAttributeName = eoAttributeName;
439 }
440 }
441 }