org.apache.cayenne.util.EntityMergeSupport Maven / Gradle / Ivy
/*****************************************************************
* 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.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.Entity;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.map.naming.BasicNamingStrategy;
import org.apache.cayenne.map.naming.NamingStrategy;
/**
* Implements methods for entity merging.
*/
public class EntityMergeSupport {
protected DataMap map;
protected boolean removeMeaningfulFKs;
protected boolean removeMeaningfulPKs;
/**
* Strategy for choosing names for entities, attributes and relationships
*/
protected NamingStrategy namingStrategy;
/**
* Listeners of merge process.
*/
protected List listeners;
public EntityMergeSupport(DataMap map) {
this(map, new BasicNamingStrategy(), true);
}
/**
* @since 3.0
*/
public EntityMergeSupport(DataMap map, NamingStrategy namingStrategy,
boolean removeMeaningfulPKs) {
this.map = map;
this.removeMeaningfulFKs = true;
this.listeners = new ArrayList();
this.removeMeaningfulPKs = removeMeaningfulPKs;
this.namingStrategy = namingStrategy;
/**
* Adding a listener, so that all created ObjRelationships would have default
* delete rule
*/
addEntityMergeListener(DeleteRuleUpdater.getEntityMergeListener());
}
/**
* Updates each one of the collection of ObjEntities, adding attributes and
* relationships based on the current state of its DbEntity.
*
* @return true if any ObjEntity has changed as a result of synchronization.
* @since 1.2 changed signature to use Collection instead of List.
*/
public boolean synchronizeWithDbEntities(Collection objEntities) {
boolean changed = false;
for (ObjEntity nextEntity : objEntities) {
if (synchronizeWithDbEntity(nextEntity)) {
changed = true;
}
}
return changed;
}
/**
* Updates ObjEntity attributes and relationships based on the current state of its
* DbEntity.
*
* @return true if the ObjEntity has changed as a result of synchronization.
*/
public boolean synchronizeWithDbEntity(ObjEntity entity) {
if (entity == null || entity.getDbEntity() == null) {
return false;
}
boolean changed = false;
// synchronization on DataMap is some (weak) protection
// against simultaneous modification of the map (like double-clicking on sync
// button)
synchronized (map) {
if (removeMeaningfulFKs) {
// get rid of attributes that are now src attributes for relationships
for (DbAttribute da : getMeaningfulFKs(entity)) {
ObjAttribute oa = entity.getAttributeForDbAttribute(da);
while (oa != null) {
String attrName = oa.getName();
entity.removeAttribute(attrName);
changed = true;
oa = entity.getAttributeForDbAttribute(da);
}
}
}
// add missing attributes
for (DbAttribute da : getAttributesToAdd(entity)) {
String attrName = namingStrategy.createObjAttributeName(da);
// avoid duplicate names
attrName = NamedObjectFactory.createName(
ObjAttribute.class,
entity,
attrName);
String type = TypesMapping.getJavaBySqlType(da.getType());
ObjAttribute oa = new ObjAttribute(attrName, type, entity);
oa.setDbAttributePath(da.getName());
entity.addAttribute(oa);
fireAttributeAdded(oa);
changed = true;
}
// add missing relationships
for (DbRelationship dr : getRelationshipsToAdd(entity)) {
DbEntity dbEntity = (DbEntity) dr.getTargetEntity();
for (Entity mappedTarget : map.getMappedEntities(dbEntity)) {
// avoid duplicate names
String relationshipName = namingStrategy
.createObjRelationshipName(dr);
relationshipName = NamedObjectFactory.createName(
ObjRelationship.class,
entity,
relationshipName);
ObjRelationship or = new ObjRelationship(relationshipName);
or.addDbRelationship(dr);
or.setSourceEntity(entity);
or.setTargetEntity(mappedTarget);
entity.addRelationship(or);
fireRelationshipAdded(or);
changed = true;
}
}
}
return changed;
}
/**
* Returns a list of DbAttributes that are mapped to foreign keys.
*
* @since 1.2
*/
public Collection getMeaningfulFKs(ObjEntity objEntity) {
List fks = new ArrayList(2);
for (ObjAttribute property : objEntity.getAttributes()) {
DbAttribute column = property.getDbAttribute();
// check if adding it makes sense at all
if (column != null && column.isForeignKey()) {
fks.add(column);
}
}
return fks;
}
/**
* Returns a list of attributes that exist in the DbEntity, but are missing from the
* ObjEntity.
*/
protected List getAttributesToAdd(ObjEntity objEntity) {
List missing = new ArrayList();
Collection rels = objEntity.getDbEntity().getRelationships();
Collection incomingRels = getIncomingRelationships(objEntity
.getDbEntity());
for (DbAttribute dba : objEntity.getDbEntity().getAttributes()) {
// already there
if (objEntity.getAttributeForDbAttribute(dba) != null) {
continue;
}
// check if adding it makes sense at all
if (!removeMeaningfulPKs) {
if (dba.getName() == null) {
continue;
}
}
else {
if (dba.getName() == null || dba.isPrimaryKey()) {
continue;
}
}
// check FK's
boolean isFK = false;
Iterator rit = rels.iterator();
while (!isFK && rit.hasNext()) {
DbRelationship rel = rit.next();
for (DbJoin join : rel.getJoins()) {
if (join.getSource() == dba) {
isFK = true;
break;
}
}
}
if (!removeMeaningfulPKs) {
if (!dba.isPrimaryKey() && isFK) {
continue;
}
}
else {
if (isFK) {
continue;
}
}
// check incoming relationships
rit = incomingRels.iterator();
while (!isFK && rit.hasNext()) {
DbRelationship rel = rit.next();
for (DbJoin join : rel.getJoins()) {
if (join.getTarget() == dba) {
isFK = true;
break;
}
}
}
if (!removeMeaningfulPKs) {
if (!dba.isPrimaryKey() && isFK) {
continue;
}
}
else {
if (isFK) {
continue;
}
}
missing.add(dba);
}
return missing;
}
private Collection getIncomingRelationships(DbEntity entity) {
Collection incoming = new ArrayList();
for (DbEntity nextEntity : entity.getDataMap().getDbEntities()) {
for (DbRelationship relationship : nextEntity.getRelationships()) {
if (entity == relationship.getTargetEntity()) {
incoming.add(relationship);
}
}
}
return incoming;
}
protected List getRelationshipsToAdd(ObjEntity objEntity) {
List missing = new ArrayList();
for (DbRelationship dbrel : objEntity.getDbEntity().getRelationships()) {
// check if adding it makes sense at all
if (dbrel.getName() == null) {
continue;
}
if (objEntity.getRelationshipForDbRelationship(dbrel) == null) {
missing.add(dbrel);
}
}
return missing;
}
public DataMap getMap() {
return map;
}
public void setMap(DataMap map) {
this.map = map;
}
/**
* @since 1.2
*/
public boolean isRemoveMeaningfulFKs() {
return removeMeaningfulFKs;
}
/**
* @since 1.2
*/
public void setRemoveMeaningfulFKs(boolean removeMeaningfulFKs) {
this.removeMeaningfulFKs = removeMeaningfulFKs;
}
/**
* Registers new EntityMergeListener
*/
public void addEntityMergeListener(EntityMergeListener listener) {
listeners.add(listener);
}
/**
* Unregisters an EntityMergeListener
*/
public void removeEntityMergeListener(EntityMergeListener listener) {
listeners.remove(listener);
}
/**
* Returns registered listeners
*/
public EntityMergeListener[] getEntityMergeListeners() {
return listeners.toArray(new EntityMergeListener[0]);
}
/**
* Notifies all listeners that an ObjAttribute was added
*/
protected void fireAttributeAdded(ObjAttribute attr) {
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).objAttributeAdded(attr);
}
}
/**
* Notifies all listeners that an ObjRelationship was added
*/
protected void fireRelationshipAdded(ObjRelationship rel) {
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).objRelationshipAdded(rel);
}
}
/**
* Sets new naming strategy for reverse engineering
*/
public void setNamingStrategy(NamingStrategy strategy) {
this.namingStrategy = strategy;
}
/**
* @return naming strategy for reverse engineering
*/
public NamingStrategy getNamingStrategy() {
return namingStrategy;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy