/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne.wocompat;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.dbsync.naming.NameBuilder;
import org.apache.cayenne.exp.ExpressionException;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.map.QueryDescriptor;
import org.apache.cayenne.map.SQLTemplateDescriptor;
import org.apache.cayenne.map.SelectQueryDescriptor;
import org.apache.cayenne.query.Ordering;
import org.apache.cayenne.query.QueryMetadata;
import org.apache.cayenne.query.SortOrder;
import org.apache.cayenne.wocompat.parser.Parser;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.PredicateUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Class for converting stored Apple EOModel mapping files to Cayenne DataMaps.
*/
public class EOModelProcessor {
private static final Logger logger = LoggerFactory.getLogger(EOModelProcessor.class);
protected Predicate prototypeChecker;
public EOModelProcessor() {
prototypeChecker = new Predicate() {
@Override
public boolean evaluate(Object object) {
if (object == null) {
return false;
}
String entityName = object.toString();
return entityName.startsWith("EO") && entityName.endsWith("Prototypes");
}
};
}
/**
* @deprecated since 4.0 in favor of {@link #loadModeIndex(URL)}.
*/
@Deprecated
public Map loadModeIndex(String path) throws Exception {
return loadModeIndex(new File(path).toURI().toURL());
}
/**
* Returns index.eomodeld contents as a Map.
*
* @since 4.0
*/
// TODO: refactor EOModelHelper to provide a similar method without loading
// all entity files in memory... here we simply copied stuff from
// EOModelHelper
public Map loadModeIndex(URL url) throws Exception {
String urlString = url.toExternalForm();
if (!urlString.endsWith(".eomodeld")) {
url = new URL(urlString + ".eomodeld");
}
Parser plistParser = new Parser();
try (InputStream in = new URL(url, "index.eomodeld").openStream();) {
plistParser.ReInit(in);
return (Map) plistParser.propertyList();
}
}
/**
* @deprecated since 4.0 in favor of {@link #loadEOModel(URL)}.
*/
@Deprecated
public DataMap loadEOModel(String path) throws Exception {
return loadEOModel(path, false);
}
/**
* @deprecated since 4.0 in favor of {@link #loadEOModel(URL, boolean)}.
*/
@Deprecated
public DataMap loadEOModel(String path, boolean generateClientClass) throws Exception {
return loadEOModel(new File(path).toURI().toURL(), generateClientClass);
}
/**
* Performs EOModel loading.
*
* @param url
* URL of ".eomodeld" directory.
*/
public DataMap loadEOModel(URL url) throws Exception {
return loadEOModel(url, false);
}
/**
* Performs EOModel loading.
*
* @param url
* URL of ".eomodeld" directory.
* @param generateClientClass
* if true then loading of EOModel is java client classes aware
* and the following processing will work with Java client class
* settings of the EOModel.
*/
public DataMap loadEOModel(URL url, boolean generateClientClass) throws Exception {
EOModelHelper helper = makeHelper(url);
// create empty map
DataMap dataMap = helper.getDataMap();
// process enitities ... throw out prototypes ... for now
List modelNames = new ArrayList(helper.modelNamesAsList());
CollectionUtils.filter(modelNames, PredicateUtils.notPredicate(prototypeChecker));
Iterator it = modelNames.iterator();
while (it.hasNext()) {
String name = (String) it.next();
// create and register entity
makeEntity(helper, name, generateClientClass);
}
// now sort following inheritance hierarchy
Collections.sort(modelNames, new InheritanceComparator(dataMap));
// after all entities are loaded, process attributes
it = modelNames.iterator();
while (it.hasNext()) {
String name = (String) it.next();
EOObjEntity e = (EOObjEntity) dataMap.getObjEntity(name);
// process entity attributes
makeAttributes(helper, e);
}
// after all entities are loaded, process relationships
it = modelNames.iterator();
while (it.hasNext()) {
String name = (String) it.next();
makeRelationships(helper, dataMap.getObjEntity(name));
}
// after all normal relationships are loaded, process flattened
// relationships
it = modelNames.iterator();
while (it.hasNext()) {
String name = (String) it.next();
makeFlatRelationships(helper, dataMap.getObjEntity(name));
}
// now create missing reverse DB (but not OBJ) relationships
// since Cayenne requires them
it = modelNames.iterator();
while (it.hasNext()) {
String name = (String) it.next();
DbEntity dbEntity = dataMap.getObjEntity(name).getDbEntity();
if (dbEntity != null) {
makeReverseDbRelationships(dbEntity);
}
}
// build SelectQueries out of EOFetchSpecifications...
it = modelNames.iterator();
while (it.hasNext()) {
String name = (String) it.next();
Iterator queries = helper.queryNames(name);
while (queries.hasNext()) {
String queryName = (String) queries.next();
EOObjEntity entity = (EOObjEntity) dataMap.getObjEntity(name);
makeQuery(helper, entity, queryName);
}
}
return dataMap;
}
/**
* Returns whether an Entity is an EOF EOPrototypes entity. According to EOF
* conventions EOPrototypes and EO[Adapter]Prototypes entities are
* considered to be prototypes.
*
* @since 1.1
*/
protected boolean isPrototypesEntity(String entityName) {
return prototypeChecker.evaluate(entityName);
}
/**
* Creates an returns new EOModelHelper to process EOModel. Exists mostly
* for the benefit of subclasses.
*/
protected EOModelHelper makeHelper(URL url) throws Exception {
return new EOModelHelper(url);
}
/**
* Creates a Cayenne query out of EOFetchSpecification data.
*
* @since 1.1
*/
protected QueryDescriptor makeQuery(EOModelHelper helper, EOObjEntity entity, String queryName) {
DataMap dataMap = helper.getDataMap();
Map queryPlist = helper.queryPListMap(entity.getName(), queryName);
if (queryPlist == null) {
return null;
}
QueryDescriptor query;
if (queryPlist.containsKey("hints")) { // just a predefined SQL query
query = makeEOSQLQueryDescriptor(entity, queryPlist);
} else {
query = makeEOQueryDescriptor(entity, queryPlist);
}
query.setName(entity.qualifiedQueryName(queryName));
dataMap.addQueryDescriptor(query);
return query;
}
protected QueryDescriptor makeEOQueryDescriptor(ObjEntity root, Map plistMap) {
SelectQueryDescriptor descriptor = QueryDescriptor.selectQueryDescriptor();
descriptor.setRoot(root);
descriptor.setDistinct("YES".equalsIgnoreCase((String) plistMap.get("usesDistinct")));
Object fetchLimit = plistMap.get("fetchLimit");
if (fetchLimit != null) {
try {
if (fetchLimit instanceof Number) {
descriptor.setProperty(QueryMetadata.FETCH_LIMIT_PROPERTY,
String.valueOf(((Number) fetchLimit).intValue()));
} else if (StringUtils.isNumeric(fetchLimit.toString())) {
descriptor.setProperty(QueryMetadata.FETCH_LIMIT_PROPERTY, fetchLimit.toString());
}
} catch (NumberFormatException nfex) {
// ignoring...
}
}
// sort orderings
List<Map<String, String>> orderings = (List<Map<String, String>>) plistMap.get("sortOrderings");
if (orderings != null && !orderings.isEmpty()) {
for (Map<String, String> ordering : orderings) {
boolean asc = !"compareDescending:".equals(ordering.get("selectorName"));
String key = ordering.get("key");
if (key != null) {
descriptor.addOrdering(new Ordering(key, asc ? SortOrder.ASCENDING : SortOrder.DESCENDING));
}
}
}
// qualifiers
Map<String, ?> qualifierMap = (Map<String, ?>) plistMap.get("qualifier");
if (qualifierMap != null && !qualifierMap.isEmpty()) {
descriptor.setQualifier(EOQuery.EOFetchSpecificationParser.makeQualifier((EOObjEntity) root, qualifierMap));
}
// prefetches
List prefetches = (List) plistMap.get("prefetchingRelationshipKeyPaths");
if (prefetches != null && !prefetches.isEmpty()) {
Iterator it = prefetches.iterator();
while (it.hasNext()) {
descriptor.addPrefetch((String) it.next());
}
}
// data rows - note that we do not support fetching individual columns
// in the
// modeler...
if (plistMap.containsKey("rawRowKeyPaths")) {
descriptor.setProperty(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, String.valueOf(true));
}
return descriptor;
}
protected QueryDescriptor makeEOSQLQueryDescriptor(ObjEntity root, Map plistMap) {
SQLTemplateDescriptor descriptor = QueryDescriptor.sqlTemplateDescriptor();
descriptor.setRoot(root);
Object fetchLimit = plistMap.get("fetchLimit");
if (fetchLimit != null) {
try {
if (fetchLimit instanceof Number) {
descriptor.setProperty(QueryMetadata.FETCH_LIMIT_PROPERTY,
String.valueOf(((Number) fetchLimit).intValue()));
} else if (StringUtils.isNumeric(fetchLimit.toString())) {
descriptor.setProperty(QueryMetadata.FETCH_LIMIT_PROPERTY, fetchLimit.toString());
}
} catch (NumberFormatException nfex) {
// ignoring...
}
}
//query
// TODO: doesn't work with Stored Procedures.
Map hints = (Map) plistMap.get("hints");
if (hints != null && !hints.isEmpty()) {
String sqlExpression = (String) hints.get("EOCustomQueryExpressionHintKey");
if (sqlExpression != null) {
descriptor.setSql(sqlExpression);
}
}
return descriptor;
}
/**
* Creates and returns a new ObjEntity linked to a corresponding DbEntity.
*/
protected EOObjEntity makeEntity(EOModelHelper helper, String name, boolean generateClientClass) {
DataMap dataMap = helper.getDataMap();
Map entityPlist = helper.entityPListMap(name);
// create ObjEntity
EOObjEntity objEntity = new EOObjEntity(name);
objEntity.setEoMap(entityPlist);
objEntity.setServerOnly(!generateClientClass);
String parent = (String) entityPlist.get("parent");
objEntity.setClassName(helper.entityClass(name, generateClientClass));
if (parent != null) {
objEntity.setSubclass(true);
objEntity.setSuperClassName(helper.entityClass(parent, generateClientClass));
}
// add flag whether this entity is set as abstract in the model
objEntity.setAbstractEntity("Y".equals(entityPlist.get("isAbstractEntity")));
// create DbEntity...since EOF allows the same table to be
// associated with multiple EOEntities, check for name duplicates
String dbEntityName = (String) entityPlist.get("externalName");
if (dbEntityName != null) {
// ... if inheritance is involved and parent hierarchy uses the same
// DBEntity,
// do not create a DbEntity...
boolean createDbEntity = true;
if (parent != null) {
String parentName = parent;
while (parentName != null) {
Map parentData = helper.entityPListMap(parentName);
if (parentData == null) {
break;
}
String parentExternalName = (String) parentData.get("externalName");
if (parentExternalName == null) {
parentName = (String) parentData.get("parent");
continue;
}
if (dbEntityName.equals(parentExternalName)) {
createDbEntity = false;
}
break;
}
}
if (createDbEntity) {
int i = 0;
String dbEntityBaseName = dbEntityName;
while (dataMap.getDbEntity(dbEntityName) != null) {
dbEntityName = dbEntityBaseName + i++;
}
objEntity.setDbEntityName(dbEntityName);
DbEntity de = new DbEntity(dbEntityName);
dataMap.addDbEntity(de);
}
}
// set various flags
objEntity.setReadOnly("Y".equals(entityPlist.get("isReadOnly")));
objEntity.setSuperEntityName((String) entityPlist.get("parent"));
dataMap.addObjEntity(objEntity);
return objEntity;
}
private static boolean externalTypeIsClob(String type) {
if( type == null ) return false;
return "CLOB".equalsIgnoreCase(type) || "TEXT".equalsIgnoreCase(type);
}
/**
* Create ObjAttributes of the specified entity, as well as DbAttributes of
* the corresponding DbEntity.
*/
protected void makeAttributes(EOModelHelper helper, EOObjEntity objEntity) {
Map entityPlistMap = helper.entityPListMap(objEntity.getName());
List primaryKeys = (List) entityPlistMap.get("primaryKeyAttributes");
List classProperties;
if (objEntity.isServerOnly()) {
classProperties = (List) entityPlistMap.get("classProperties");
} else {
classProperties = (List) entityPlistMap.get("clientClassProperties");
}
List attributes = (List) entityPlistMap.get("attributes");
DbEntity dbEntity = objEntity.getDbEntity();
if (primaryKeys == null) {
primaryKeys = Collections.EMPTY_LIST;
}
if (classProperties == null) {
classProperties = Collections.EMPTY_LIST;
}
if (attributes == null) {
attributes = Collections.EMPTY_LIST;
}
// detect single table inheritance
boolean singleTableInheritance = false;
String parentName = (String) entityPlistMap.get("parent");
while (parentName != null) {
Map parentData = helper.entityPListMap(parentName);
if (parentData == null) {
break;
}
String parentExternalName = (String) parentData.get("externalName");
if (parentExternalName == null) {
parentName = (String) parentData.get("parent");
continue;
}
if (dbEntity.getName() != null && dbEntity.getName().equals(parentExternalName)) {
singleTableInheritance = true;
}
break;
}
Iterator it = attributes.iterator();
while (it.hasNext()) {
Map attrMap = (Map) it.next();
String prototypeName = (String) attrMap.get("prototypeName");
Map prototypeAttrMap = helper.getPrototypeAttributeMapFor(prototypeName);
String dbAttrName = getStringValueFromMap( "columnName", attrMap, prototypeAttrMap );
String attrName = getStringValueFromMap( "name", attrMap, prototypeAttrMap );
String attrType = getStringValueFromMap( "valueClassName", attrMap, prototypeAttrMap );
String valueType = getStringValueFromMap( "valueType", attrMap, prototypeAttrMap );
String externalType = getStringValueFromMap( "externalType", attrMap, prototypeAttrMap );
String javaType = helper.javaTypeForEOModelerType(attrType, valueType);
EODbAttribute dbAttr = null;
if (dbAttrName != null && dbEntity != null) {
// if inherited attribute, skip it for DbEntity...
if (!singleTableInheritance || dbEntity.getAttribute(dbAttrName) == null) {
// create DbAttribute...since EOF allows the same column name for
// more than one Java attribute, we need to check for name duplicates
int i = 0;
String dbAttributeBaseName = dbAttrName;
while (dbEntity.getAttribute(dbAttrName) != null) {
dbAttrName = dbAttributeBaseName + i++;
}
int sqlType = TypesMapping.getSqlTypeByJava(javaType);
int width = getInt("width", attrMap, prototypeAttrMap, -1);
if (sqlType == Types.VARCHAR && width < 0 && externalTypeIsClob(externalType)) {
// CLOB, or TEXT as PostgreSQL calls it, is usally noted as having no width. In order to
// not mistake any VARCHAR columns that just happen to have no width set in the model
// for CLOB columns, use externalType as an additional check.
sqlType = Types.CLOB;
} else if(sqlType == TypesMapping.NOT_DEFINED && externalType != null) {
// At this point we usually hit a custom Java class through a prototype, which isn't resolvable
// with the model alone. But we can use the externalType as a hint. If that still doesn't match
// anything, sqlType will still be NOT_DEFINED.
sqlType = TypesMapping.getSqlTypeByName(externalType.toUpperCase());
}
dbAttr = new EODbAttribute(dbAttrName, sqlType, dbEntity);
dbAttr.setEoAttributeName(attrName);
dbEntity.addAttribute(dbAttr);
if (width >= 0) {
dbAttr.setMaxLength(width);
}
int scale = getInt("scale", attrMap, prototypeAttrMap, -1);
if (scale >= 0) {
dbAttr.setScale(scale);
}
if (primaryKeys.contains(attrName))
dbAttr.setPrimaryKey(true);
Object allowsNull = attrMap.get("allowsNull");
// TODO: Unclear that allowsNull should be inherited from EOPrototypes
// if (null == allowsNull) allowsNull = prototypeAttrMap.get("allowsNull");;
dbAttr.setMandatory(!"Y".equals(allowsNull));
}
}
if (classProperties.contains(attrName)) {
EOObjAttribute attr = new EOObjAttribute(attrName, javaType, objEntity);
// set readOnly flag of Attribute if either attribute is read or
// if entity is readOnly
String entityReadOnlyString = (String) entityPlistMap.get("isReadOnly");
String attributeReadOnlyString = (String) attrMap.get("isReadOnly");
if ("Y".equals(entityReadOnlyString) || "Y".equals(attributeReadOnlyString)) {
attr.setReadOnly(true);
}
// set name instead of the actual attribute, as it may be
// inherited....
attr.setDbAttributePath(dbAttrName);
objEntity.addAttribute(attr);
}
}
}
private String getStringValueFromMap( String key, Map attrMap, Map prototypeAttrMap ) {
String dbAttrName = (String) attrMap.get(key);
if (null == dbAttrName) {
dbAttrName = (String) prototypeAttrMap.get(key);
}
return dbAttrName;
}
int getInt(String key, Map map, Map prototypes, int defaultValue) {
Object value = map.get(key);
if (value == null) {
value = prototypes.get(key);
}
if (value == null) {
return defaultValue;
}
// per CAY-752, value can be a String or a Number, so handle both
if (value instanceof Number) {
return ((Number) value).intValue();
} else {
try {
return Integer.parseInt(value.toString());
} catch (NumberFormatException nfex) {
return defaultValue;
}
}
}
/**
* Create ObjRelationships of the specified entity, as well as
* DbRelationships of the corresponding DbEntity.
*/
protected void makeRelationships(EOModelHelper helper, ObjEntity objEntity) {
Map entityPlistMap = helper.entityPListMap(objEntity.getName());
List classProps = (List) entityPlistMap.get("classProperties");
List rinfo = (List) entityPlistMap.get("relationships");
Collection attributes = (Collection) entityPlistMap.get("attributes");
if (rinfo == null) {
return;
}
if (classProps == null) {
classProps = Collections.EMPTY_LIST;
}
if (attributes == null) {
attributes = Collections.EMPTY_LIST;
}
DbEntity dbSrc = objEntity.getDbEntity();
Iterator it = rinfo.iterator();
while (it.hasNext()) {
Map relMap = (Map) it.next();
String targetName = (String) relMap.get("destination");
// ignore flattened relationships for now
if (targetName == null) {
continue;
}
String relName = (String) relMap.get("name");
boolean toMany = "Y".equals(relMap.get("isToMany"));
boolean toDependentPK = "Y".equals(relMap.get("propagatesPrimaryKey"));
ObjEntity target = helper.getDataMap().getObjEntity(targetName);
// target maybe null for cross-EOModel relationships
// ignoring those now.
if (target == null) {
continue;
}
DbEntity dbTarget = target.getDbEntity();
Map targetPlistMap = helper.entityPListMap(targetName);
Collection targetAttributes = (Collection) targetPlistMap.get("attributes");
DbRelationship dbRel = null;
// process underlying DbRelationship
// Note: there is no flattened rel. support here....
// Note: source maybe null, e.g. an abstract entity.
if (dbSrc != null && dbTarget != null) {
// in case of inheritance EOF stores duplicates of all inherited
// relationships, so we must skip this relationship in DB entity
// if it is
// already there...
dbRel = dbSrc.getRelationship(relName);
if (dbRel == null) {
dbRel = new DbRelationship();
dbRel.setSourceEntity(dbSrc);
dbRel.setTargetEntityName(dbTarget);
dbRel.setToMany(toMany);
dbRel.setName(relName);
dbRel.setToDependentPK(toDependentPK);
dbSrc.addRelationship(dbRel);
List joins = (List) relMap.get("joins");
Iterator jIt = joins.iterator();
while (jIt.hasNext()) {
Map joinMap = (Map) jIt.next();
DbJoin join = new DbJoin(dbRel);
// find source attribute dictionary and extract the
// column name
String sourceAttributeName = (String) joinMap.get("sourceAttribute");
join.setSourceName(columnName(attributes, sourceAttributeName));
String targetAttributeName = (String) joinMap.get("destinationAttribute");
join.setTargetName(columnName(targetAttributes, targetAttributeName));
dbRel.addJoin(join);
}
}
}
// only create obj relationship if it is a class property
if (classProps.contains(relName)) {
ObjRelationship rel = new ObjRelationship();
rel.setName(relName);
rel.setSourceEntity(objEntity);
rel.setTargetEntityName(target);
objEntity.addRelationship(rel);
if (dbRel != null) {
rel.addDbRelationship(dbRel);
}
}
}
}
/**
* Create reverse DbRelationships that were not created so far, since
* Cayenne requires them.
*
* @since 1.0.5
*/
protected void makeReverseDbRelationships(DbEntity dbEntity) {
if (dbEntity == null) {
throw new NullPointerException("Attempt to create reverse relationships for the null DbEntity.");
}
// iterate over a copy of the collection, since in case of
// reflexive relationships, we may modify source entity relationship map
for (DbRelationship relationship : new ArrayList<>(dbEntity.getRelationships())) {
if (relationship.getReverseRelationship() == null) {
DbRelationship reverse = relationship.createReverseRelationship();
reverse.setName(NameBuilder.builder(reverse, reverse.getSourceEntity())
// TODO: we can do better with ObjectNameGenerator
.baseName(relationship.getName() + "Reverse")
.name());
relationship.getTargetEntity().addRelationship(reverse);
}
}
}
/**
* Create Flattened ObjRelationships of the specified entity.
*/
protected void makeFlatRelationships(EOModelHelper helper, ObjEntity e) {
Map info = helper.entityPListMap(e.getName());
List rinfo = (List) info.get("relationships");
if (rinfo == null) {
return;
}
Iterator it = rinfo.iterator();
while (it.hasNext()) {
Map relMap = (Map) it.next();
String targetPath = (String) relMap.get("definition");
// ignore normal relationships
if (targetPath == null) {
continue;
}
ObjRelationship flatRel = new ObjRelationship();
flatRel.setName((String) relMap.get("name"));
flatRel.setSourceEntity(e);
try {
flatRel.setDbRelationshipPath(targetPath);
} catch (ExpressionException ex) {
logger.warn("Invalid relationship: " + targetPath);
continue;
}
// find target entity
Map entityInfo = info;
StringTokenizer toks = new StringTokenizer(targetPath, ".");
while (toks.hasMoreTokens() && entityInfo != null) {
String pathComponent = toks.nextToken();
// get relationship info and reset entityInfo, so that we could
// use
// entityInfo state as an indicator of valid flat relationship
// enpoint
// outside the loop
Collection relationshipInfo = (Collection) entityInfo.get("relationships");
entityInfo = null;
if (relationshipInfo == null) {
break;
}
Iterator rit = relationshipInfo.iterator();
while (rit.hasNext()) {
Map pathRelationship = (Map) rit.next();
if (pathComponent.equals(pathRelationship.get("name"))) {
String targetName = (String) pathRelationship.get("destination");
entityInfo = helper.entityPListMap(targetName);
break;
}
}
}
if (entityInfo != null) {
flatRel.setTargetEntityName((String) entityInfo.get("name"));
}
e.addRelationship(flatRel);
}
}
/**
* Locates an attribute map matching the name and returns column name for
* this attribute.
*
* @since 1.1
*/
String columnName(Collection entityAttributes, String attributeName) {
if (attributeName == null) {
return null;
}
Iterator it = entityAttributes.iterator();
while (it.hasNext()) {
Map map = (Map) it.next();
if (attributeName.equals(map.get("name"))) {
return (String) map.get("columnName");
}
}
return null;
}
// sorts ObjEntities so that subentities in inheritance hierarchy are shown
// last
final class InheritanceComparator implements Comparator {
DataMap dataMap;
InheritanceComparator(DataMap dataMap) {
this.dataMap = dataMap;
}
@Override
public int compare(Object o1, Object o2) {
if (o1 == null) {
return o2 != null ? -1 : 0;
} else if (o2 == null) {
return 1;
}
String name1 = o1.toString();
String name2 = o2.toString();
ObjEntity e1 = dataMap.getObjEntity(name1);
ObjEntity e2 = dataMap.getObjEntity(name2);
return compareEntities(e1, e2);
}
int compareEntities(ObjEntity e1, ObjEntity e2) {
if (e1 == null) {
return e2 != null ? -1 : 0;
} else if (e2 == null) {
return 1;
}
// entity goes first if it is a direct or indirect superentity of
// another
// one
if (e1.isSubentityOf(e2)) {
return 1;
}
if (e2.isSubentityOf(e1)) {
return -1;
}
// sort alphabetically
return e1.getName().compareTo(e2.getName());
}
}
}