/***************************************************************** * 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.map; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.ConfigurationNodeVisitor; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.map.event.DbEntityListener; import org.apache.cayenne.map.event.EntityEvent; import org.apache.cayenne.map.event.ObjEntityListener; import org.apache.cayenne.resource.Resource; import org.apache.cayenne.util.ToStringBuilder; import org.apache.cayenne.util.Util; import org.apache.cayenne.util.XMLEncoder; import org.apache.cayenne.util.XMLSerializable; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.HashMap; import static java.util.Collections.emptyList; /** * Stores a collection of related mapping objects that describe database and * object layers of an application. DataMap contains DbEntities mapping database * tables, ObjEntities - mapping persistent Java classes, Procedures - mapping * database stored procedures. */ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable, MappingNamespace, DbEntityListener, ObjEntityListener, Comparable<DataMap> { private static final long serialVersionUID = 4851901426473991657L; /** * Defines whether a DataMap supports client entities. * * @since 1.2 */ public static final String CLIENT_SUPPORTED_PROPERTY = "clientSupported"; /** * Defines the name of the property for default client Java class package. * * @since 1.2 */ public static final String DEFAULT_CLIENT_PACKAGE_PROPERTY = "defaultClientPackage"; /** * Defines the name of the property for default client Java superclass. * * @since 3.0 */ public static final String DEFAULT_CLIENT_SUPERCLASS_PROPERTY = "defaultClientSuperclass"; /** * Defines the name of the property for default DB catalog. * * @since 4.0 */ public static final String DEFAULT_CATALOG_PROPERTY = "defaultCatalog"; /** * Defines the name of the property for default DB schema. * * @since 1.1 */ public static final String DEFAULT_SCHEMA_PROPERTY = "defaultSchema"; /** * Defines the name of the property for default Java class package. * * @since 1.1 */ public static final String DEFAULT_PACKAGE_PROPERTY = "defaultPackage"; /** * Defines the name of the property for default Java superclass. * * @since 1.1 */ public static final String DEFAULT_SUPERCLASS_PROPERTY = "defaultSuperclass"; /** * Defines the name of the property for default DB schema. * * @since 1.1 */ public static final String DEFAULT_LOCK_TYPE_PROPERTY = "defaultLockType"; public static final String DEFAULT_QUOTE_SQL_IDENTIFIERS_PROPERTY = "quoteSqlIdentifiers"; /** * The namespace in which the data map XML file will be created. This is * also the URI to locate a copy of the schema document. */ public static final String SCHEMA_XSD = "http://cayenne.apache.org/schema/9/modelMap"; protected String name; protected String location; protected MappingNamespace namespace; protected Boolean quotingSQLIdentifiers; protected String defaultCatalog; protected String defaultSchema; protected String defaultPackage; protected String defaultSuperclass; protected int defaultLockType; protected boolean clientSupported; protected String defaultClientPackage; protected String defaultClientSuperclass; private SortedMap<String, Embeddable> embeddablesMap; private SortedMap<String, ObjEntity> objEntityMap; private SortedMap<String, DbEntity> dbEntityMap; private SortedMap<String, Procedure> procedureMap; private SortedMap<String, QueryDescriptor> queryDescriptorMap; private SortedMap<String, SQLResult> results; /** * @deprecated since 4.0 unused as listeners are no longer tied to a * DataMap. */ private List<EntityListener> defaultEntityListeners; /** * @since 3.1 */ protected transient Resource configurationSource; /** * @since 3.1 */ protected DataChannelDescriptor dataChannelDescriptor; /** * Creates a new unnamed DataMap. */ public DataMap() { this(null); } /** * Creates a new named DataMap. */ public DataMap(String mapName) { this(mapName, Collections.<String, Object> emptyMap()); } public DataMap(String mapName, Map<String, Object> properties) { embeddablesMap = new TreeMap<String, Embeddable>(); objEntityMap = new TreeMap<String, ObjEntity>(); dbEntityMap = new TreeMap<String, DbEntity>(); procedureMap = new TreeMap<String, Procedure>(); queryDescriptorMap = new TreeMap<>(); defaultEntityListeners = new ArrayList<>(3); results = new TreeMap<String, SQLResult>(); setName(mapName); initWithProperties(properties); } /** * @since 3.1 */ public DataChannelDescriptor getDataChannelDescriptor() { return dataChannelDescriptor; } /** * @since 3.1 */ public void setDataChannelDescriptor(DataChannelDescriptor dataChannelDescriptor) { this.dataChannelDescriptor = dataChannelDescriptor; } /** * @since 3.1 */ public <T> T acceptVisitor(ConfigurationNodeVisitor<T> visitor) { return visitor.visitDataMap(this); } /** * @since 3.1 */ public int compareTo(DataMap o) { String o1 = getName(); String o2 = o.getName(); if (o1 == null) { return (o2 != null) ? -1 : 0; } else if (o2 == null) { return 1; } else { return o1.compareTo(o2); } } /** * @since 3.0 */ public boolean isQuotingSQLIdentifiers() { return quotingSQLIdentifiers; } /** * @since 3.0 */ public void setQuotingSQLIdentifiers(boolean quotingSqlIdentifiers) { this.quotingSQLIdentifiers = quotingSqlIdentifiers; } /** * Performs DataMap initialization from a set of properties, using defaults * for the missing properties. * * @since 1.1 */ public void initWithProperties(Map<String, Object> properties) { // must init defaults even if properties are empty if (properties == null) { properties = Collections.<String, Object> emptyMap(); } Object lockType = properties.get(DEFAULT_LOCK_TYPE_PROPERTY); Object packageName = properties.get(DEFAULT_PACKAGE_PROPERTY); Object catalog = properties.get(DEFAULT_CATALOG_PROPERTY); Object schema = properties.get(DEFAULT_SCHEMA_PROPERTY); Object superclass = properties.get(DEFAULT_SUPERCLASS_PROPERTY); Object clientEntities = properties.get(CLIENT_SUPPORTED_PROPERTY); Object clientPackageName = properties.get(DEFAULT_CLIENT_PACKAGE_PROPERTY); Object clientSuperclass = properties.get(DEFAULT_CLIENT_SUPERCLASS_PROPERTY); Object quoteSqlIdentifier = properties.get(DEFAULT_QUOTE_SQL_IDENTIFIERS_PROPERTY); this.defaultLockType = "optimistic".equals(lockType) ? ObjEntity.LOCK_TYPE_OPTIMISTIC : ObjEntity.LOCK_TYPE_NONE; this.defaultPackage = (packageName != null) ? packageName.toString() : null; this.quotingSQLIdentifiers = (quoteSqlIdentifier != null) ? "true".equalsIgnoreCase(quoteSqlIdentifier .toString()) : false; this.defaultSchema = (schema != null) ? schema.toString() : null; this.defaultCatalog = (catalog != null) ? catalog.toString() : null; this.defaultSuperclass = (superclass != null) ? superclass.toString() : null; this.clientSupported = (clientEntities != null) ? "true".equalsIgnoreCase(clientEntities.toString()) : false; this.defaultClientPackage = (clientPackageName != null) ? clientPackageName.toString() : null; this.defaultClientSuperclass = (clientSuperclass != null) ? clientSuperclass.toString() : null; } /** * Returns a DataMap stripped of any server-side information, such as * DbEntity mapping, or ObjEntities that are not allowed in the client tier. * Returns null if this DataMap as a whole does not support client tier * persistence. * * @since 1.2 */ public DataMap getClientDataMap(EntityResolver serverResolver) { if (!isClientSupported()) { return null; } DataMap clientMap = new DataMap(getName()); // create client entities for entities for (ObjEntity entity : getObjEntities()) { if (entity.isClientAllowed()) { clientMap.addObjEntity(entity.getClientEntity()); } } // create proxies for named queries for (QueryDescriptor q : getQueryDescriptors()) { clientMap.addQueryDescriptor(q); } return clientMap; } /** * Prints itself as XML to the provided PrintWriter. * * @since 1.1 */ public void encodeAsXML(XMLEncoder encoder) { encoder.println("<data-map xmlns=\"http://cayenne.apache.org/schema/9/modelMap\""); encoder.indent(1); encoder.println(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); encoder.println(" xsi:schemaLocation=\"" + SCHEMA_XSD + " " + SCHEMA_XSD + ".xsd\""); encoder.printProjectVersion(); encoder.println(">"); // properties if (defaultLockType == ObjEntity.LOCK_TYPE_OPTIMISTIC) { encoder.printProperty(DEFAULT_LOCK_TYPE_PROPERTY, "optimistic"); } if (!Util.isEmptyString(defaultPackage)) { encoder.printProperty(DEFAULT_PACKAGE_PROPERTY, defaultPackage); } if (!Util.isEmptyString(defaultCatalog)) { encoder.printProperty(DEFAULT_CATALOG_PROPERTY, defaultCatalog); } if (!Util.isEmptyString(defaultSchema)) { encoder.printProperty(DEFAULT_SCHEMA_PROPERTY, defaultSchema); } if (!Util.isEmptyString(defaultSuperclass)) { encoder.printProperty(DEFAULT_SUPERCLASS_PROPERTY, defaultSuperclass); } if (quotingSQLIdentifiers) { encoder.printProperty(DEFAULT_QUOTE_SQL_IDENTIFIERS_PROPERTY, quotingSQLIdentifiers); } if (clientSupported) { encoder.printProperty(CLIENT_SUPPORTED_PROPERTY, "true"); } if (!Util.isEmptyString(defaultClientPackage)) { encoder.printProperty(DEFAULT_CLIENT_PACKAGE_PROPERTY, defaultClientPackage); } if (!Util.isEmptyString(defaultClientSuperclass)) { encoder.printProperty(DEFAULT_CLIENT_SUPERCLASS_PROPERTY, defaultClientSuperclass); } // embeddables encoder.print(getEmbeddableMap()); // procedures encoder.print(getProcedureMap()); // DbEntities for (DbEntity dbe : getDbEntityMap().values()) { dbe.encodeAsXML(encoder); } // others... encoder.print(getObjEntityMap()); encodeDBRelationshipsAsXML(getDbEntityMap(), encoder); encodeOBJRelationshipsAsXML(getObjEntityMap(), encoder); for (QueryDescriptor query : getQueryDescriptors()) { query.encodeAsXML(encoder); } encoder.indent(-1); encoder.println("</data-map>"); } // stores relationships for the map of entities private final void encodeDBRelationshipsAsXML(Map<String, DbEntity> entityMap, XMLEncoder encoder) { for (Entity entity : entityMap.values()) { for (Relationship relationship : entity.getRelationships()) { // filter out synthetic if (!relationship.isRuntime()) { relationship.encodeAsXML(encoder); } } } } // stores relationships for the map of entities private final void encodeOBJRelationshipsAsXML(Map<String, ObjEntity> entityMap, XMLEncoder encoder) { for (ObjEntity entity : entityMap.values()) { for (Relationship relationship : entity.getDeclaredRelationships()) { // filter out synthetic if (!relationship.isRuntime()) { relationship.encodeAsXML(encoder); } } } } @Override public String toString() { return new ToStringBuilder(this).append("name", getName()).toString(); } /** * Returns the name of this DataMap. */ public String getName() { return name; } /** * Set the name of this DataMap. */ public void setName(String name) { this.name = name; } /** * Adds all Object and DB entities and Queries from another map to this map. * Overwrites all existing entities and queries with the new ones. * <p> * <i>TODO: will need to implement advanced merge that allows different * policies for overwriting entities / queries. </i> * </p> */ public void mergeWithDataMap(DataMap map) { for (DbEntity ent : new ArrayList<>(map.getDbEntities())) { this.removeDbEntity(ent.getName()); this.addDbEntity(ent); } for (ObjEntity ent : new ArrayList<>(map.getObjEntities())) { this.removeObjEntity(ent.getName()); this.addObjEntity(ent); } for (QueryDescriptor query : new ArrayList<>(map.getQueryDescriptors())) { this.removeQueryDescriptor(query.getName()); this.addQueryDescriptor(query); } } /** * Returns "location" property value. Location is abstract and can depend on * how the DataMap was loaded. E.g. location can be a File on the filesystem * or a location within a JAR. */ public String getLocation() { return location; } /** * Sets "location" property. */ public void setLocation(String location) { this.location = location; } /** * Returns a sorted unmodifiable map of ObjEntities contained in this * DataMap, keyed by ObjEntity name. */ public SortedMap<String, ObjEntity> getObjEntityMap() { return Collections.unmodifiableSortedMap(objEntityMap); } /** * Returns a sorted unmodifiable map of DbEntities contained in this * DataMap, keyed by DbEntity name. */ public SortedMap<String, DbEntity> getDbEntityMap() { return Collections.unmodifiableSortedMap(dbEntityMap); } /** * Returns a named query associated with this DataMap. * * @since 4.0 */ public QueryDescriptor getQueryDescriptor(String queryName) { QueryDescriptor queryDescriptor = queryDescriptorMap.get(queryName); if (queryDescriptor != null) { return queryDescriptor; } return namespace != null ? namespace.getQueryDescriptor(queryName) : null; } /** * Stores a query descriptor under its name. * * @since 1.1 */ public void addQueryDescriptor(QueryDescriptor queryDescriptor) { if (queryDescriptor == null) { throw new NullPointerException("Can't add null query."); } if (queryDescriptor.getName() == null) { throw new NullPointerException("Query name can't be null."); } // TODO: change method signature to return replaced procedure and make // sure the // Modeler handles it... QueryDescriptor existingQueryDescriptor = queryDescriptorMap.get(queryDescriptor.getName()); if (existingQueryDescriptor != null) { if (existingQueryDescriptor == queryDescriptor) { return; } else { throw new IllegalArgumentException("An attempt to override entity '" + queryDescriptor.getName()); } } queryDescriptorMap.put(queryDescriptor.getName(), queryDescriptor); } /** * Removes a named query from the DataMap. * * @since 4.0 */ public void removeQueryDescriptor(String queryName) { queryDescriptorMap.remove(queryName); } /** * Removes all stored embeddable objects from the map. * * @since 3.0 */ public void clearEmbeddables() { embeddablesMap.clear(); } /** * @since 3.0 */ public void clearResultSets() { results.clear(); } /** * @since 1.1 */ public void clearQueries() { queryDescriptorMap.clear(); } /** * @since 1.2 */ public void clearObjEntities() { objEntityMap.clear(); } /** * @since 1.2 */ public void clearDbEntities() { dbEntityMap.clear(); } /** * @since 1.2 */ public void clearProcedures() { procedureMap.clear(); } /** * @since 4.0 */ public SortedMap<String, QueryDescriptor> getQueryDescriptorMap() { return Collections.unmodifiableSortedMap(queryDescriptorMap); } /** * Returns an unmodifiable collection of mapped queries. * * @since 4.0 */ public Collection<QueryDescriptor> getQueryDescriptors() { return Collections.unmodifiableCollection(queryDescriptorMap.values()); } /** * Adds an embeddable object to the DataMap. * * @since 3.0 */ public void addEmbeddable(Embeddable embeddable) { if (embeddable == null) { throw new NullPointerException("Null embeddable"); } if (embeddable.getClassName() == null) { throw new NullPointerException("Attempt to add Embeddable with no class name."); } // TODO: change method signature to return replaced entity and make sure // the // Modeler handles it... Object existing = embeddablesMap.get(embeddable.getClassName()); if (existing != null) { if (existing == embeddable) { return; } else { throw new IllegalArgumentException("An attempt to override embeddable '" + embeddable.getClassName()); } } embeddablesMap.put(embeddable.getClassName(), embeddable); embeddable.setDataMap(this); } /** * Adds a named SQLResultSet to the DataMap. * * @since 3.0 */ public void addResult(SQLResult result) { if (result == null) { throw new NullPointerException("Null result"); } if (result.getName() == null) { throw new NullPointerException("Attempt to add resultSetMapping with no name."); } Object existing = results.get(result.getName()); if (existing != null) { if (existing == result) { return; } else { throw new IllegalArgumentException("An attempt to override resultSetMapping '" + result.getName()); } } results.put(result.getName(), result); } /** * Adds a new ObjEntity to this DataMap. */ public void addObjEntity(ObjEntity entity) { if (entity.getName() == null) { throw new NullPointerException("Attempt to add ObjEntity with no name."); } // TODO: change method signature to return replaced entity and make sure // the // Modeler handles it... Object existingEntity = objEntityMap.get(entity.getName()); if (existingEntity != null) { if (existingEntity == entity) { return; } else { throw new IllegalArgumentException("An attempt to override entity '" + entity.getName()); } } objEntityMap.put(entity.getName(), entity); entity.setDataMap(this); } /** * Adds a new DbEntity to this DataMap. */ public void addDbEntity(DbEntity entity) { if (entity.getName() == null) { throw new NullPointerException("Attempt to add DbEntity with no name."); } // TODO: change method signature to return replaced entity and make sure // the // Modeler handles it... Object existingEntity = dbEntityMap.get(entity.getName()); if (existingEntity != null) { if (existingEntity == entity) { return; } else { throw new IllegalArgumentException("An attempt to override db entity '" + entity.getName() + "' in DataMap '" + getName() + "'"); } } dbEntityMap.put(entity.getName(), entity); entity.setDataMap(this); } /** * Returns an unmodifiable collection of ObjEntities stored in this DataMap. */ public Collection<ObjEntity> getObjEntities() { return Collections.unmodifiableCollection(objEntityMap.values()); } /** * @since 3.0 */ public Map<String, Embeddable> getEmbeddableMap() { return Collections.unmodifiableMap(embeddablesMap); } /** * Returns a collection of {@link Embeddable} mappings stored in the * DataMap. * * @since 3.0 */ public Collection<Embeddable> getEmbeddables() { return Collections.unmodifiableCollection(embeddablesMap.values()); } /** * @since 3.0 */ public Map<String, SQLResult> getResultsMap() { return Collections.unmodifiableMap(results); } /** * @since 3.0 */ public Collection<SQLResult> getResults() { return Collections.unmodifiableCollection(results.values()); } /** * @since 3.0 */ public Embeddable getEmbeddable(String className) { Embeddable e = embeddablesMap.get(className); if (e != null) { return e; } return namespace != null ? namespace.getEmbeddable(className) : null; } /** * @since 3.0 */ public SQLResult getResult(String name) { SQLResult rsMapping = results.get(name); if (rsMapping != null) { return rsMapping; } return namespace != null ? namespace.getResult(name) : null; } /** * Returns an unmodifiable list of default {@link EntityListener} objects. * Note that since the order of listeners is significant a list, not just a * generic Collection is returned. * * @since 3.0 * @deprecated since 4.0 unused as listeners are no longer tied to a * DataMap. */ @Deprecated public List<EntityListener> getDefaultEntityListeners() { return Collections.unmodifiableList(defaultEntityListeners); } /** * Adds a new EntityListener. * * @since 3.0 * @throws IllegalArgumentException * if a listener for the same class name is already registered. * @deprecated since 4.0 unused as listeners are no longer tied to a * DataMap. */ @Deprecated public void addDefaultEntityListener(EntityListener listener) { for (EntityListener next : defaultEntityListeners) { if (listener.getClassName().equals(next.getClassName())) { throw new IllegalArgumentException("Duplicate default listener for " + next.getClassName()); } } defaultEntityListeners.add(listener); } /** * Removes a listener matching class name. * * @since 3.0 * @deprecated since 4.0 unused as listeners are no longer tied to a * DataMap. */ @Deprecated public void removeDefaultEntityListener(String className) { Iterator<EntityListener> it = defaultEntityListeners.iterator(); while (it.hasNext()) { EntityListener next = it.next(); if (className.equals(next.getClassName())) { it.remove(); break; } } } /** * @since 3.0 * @deprecated since 4.0 unused, as listeners are no longer tied to a * DataMap. */ @Deprecated public EntityListener getDefaultEntityListener(String className) { for (EntityListener listener : defaultEntityListeners) { if (className.equals(listener.getClassName())) { return listener; } } return null; } /** * Returns all DbEntities in this DataMap. */ public Collection<DbEntity> getDbEntities() { return Collections.unmodifiableCollection(dbEntityMap.values()); } /** * Returns DbEntity matching the <code>name</code> parameter. No * dependencies will be searched. */ public DbEntity getDbEntity(String dbEntityName) { DbEntity entity = dbEntityMap.get(dbEntityName); if (entity != null) { return entity; } return namespace != null ? namespace.getDbEntity(dbEntityName) : null; } /** * Returns an ObjEntity for a DataObject class name. * * @since 1.1 */ public ObjEntity getObjEntityForJavaClass(String javaClassName) { if (javaClassName == null) { return null; } for (ObjEntity entity : getObjEntities()) { if (javaClassName.equals(entity.getClassName())) { return entity; } } return null; } /** * Returns an ObjEntity for a given name. If it is not found in this * DataMap, it will search a parent EntityNamespace. */ public ObjEntity getObjEntity(String objEntityName) { ObjEntity entity = objEntityMap.get(objEntityName); if (entity != null) { return entity; } return namespace != null ? namespace.getObjEntity(objEntityName) : null; } /** * Returns all ObjEntities mapped to the given DbEntity. */ public Collection<ObjEntity> getMappedEntities(DbEntity dbEntity) { if (dbEntity == null) { return emptyList(); } Collection<ObjEntity> allEntities = (namespace != null) ? namespace.getObjEntities() : getObjEntities(); if (allEntities.isEmpty()) { return emptyList(); } Collection<ObjEntity> result = new ArrayList<>(); for (ObjEntity entity : allEntities) { if (entity.getDbEntity() == dbEntity) { result.add(entity); } } return result; } /** * Removes an {@link Embeddable} descriptor with matching class name. * * @since 3.0 */ public void removeEmbeddable(String className) { // TODO: andrus, 1/25/2007 - clean up references like removeDbEntity // does. embeddablesMap.remove(className); } /** * @since 3.0 */ public void removeResult(String name) { results.remove(name); } /** * "Dirty" remove of the DbEntity from the data map. */ public void removeDbEntity(String dbEntityName) { removeDbEntity(dbEntityName, false); } /** * Removes DbEntity from the DataMap. If <code>clearDependencies</code> is * true, all DbRelationships that reference this entity are also removed. * ObjEntities that rely on this entity are cleaned up. * * @since 1.1 */ public void removeDbEntity(String dbEntityName, boolean clearDependencies) { DbEntity dbEntityToDelete = dbEntityMap.remove(dbEntityName); if (dbEntityToDelete != null && clearDependencies) { for (DbEntity dbEnt : this.getDbEntities()) { // take a copy since we're going to modify the entity for (Relationship rel : new ArrayList<>(dbEnt.getRelationships())) { if (dbEntityName.equals(rel.getTargetEntityName())) { dbEnt.removeRelationship(rel.getName()); } } } // Remove all obj relationships referencing removed DbRelationships. for (ObjEntity objEnt : this.getObjEntities()) { if (dbEntityToDelete.getName().equals(objEnt.getDbEntityName())) { objEnt.clearDbMapping(); } else { for (ObjRelationship rel : objEnt.getRelationships()) { for (DbRelationship dbRel : rel.getDbRelationships()) { if (dbRel.getTargetEntity() == dbEntityToDelete) { rel.clearDbRelationships(); break; } } } } } MappingNamespace ns = getNamespace(); if (ns instanceof EntityResolver) { ((EntityResolver) ns).refreshMappingCache(); } } } /** * "Dirty" remove of the ObjEntity from the data map. */ public void removeObjEntity(String objEntityName) { removeObjEntity(objEntityName, false); } /** * Removes ObjEntity from the DataMap. If <code>clearDependencies</code> is * true, all ObjRelationships that reference this entity are also removed. * * @since 1.1 */ public void removeObjEntity(String objEntityName, boolean clearDependencies) { ObjEntity entity = objEntityMap.remove(objEntityName); if (entity != null && clearDependencies) { // remove relationships that point to this entity for (ObjEntity ent : getObjEntities()) { // take a copy since we're going to modify the entity for (Relationship relationship : new ArrayList<>(ent.getRelationships())) { if (objEntityName.equals(relationship.getTargetEntityName()) || objEntityName.equals(relationship.getTargetEntityName())) { ent.removeRelationship(relationship.getName()); } } } MappingNamespace ns = getNamespace(); if (ns instanceof EntityResolver) { ((EntityResolver) ns).refreshMappingCache(); } } } /** * Returns stored procedures associated with this DataMap. */ public Collection<Procedure> getProcedures() { return Collections.unmodifiableCollection(procedureMap.values()); } /** * Returns a Procedure for a given name or null if no such procedure exists. * If Procedure is not found in this DataMap, a parent EntityNamcespace is * searched. */ public Procedure getProcedure(String procedureName) { Procedure procedure = procedureMap.get(procedureName); if (procedure != null) { return procedure; } return namespace != null ? namespace.getProcedure(procedureName) : null; } /** * Adds stored procedure to the list of procedures. If there is another * procedure registered under the same name, throws an * IllegalArgumentException. */ public void addProcedure(Procedure procedure) { if (procedure.getName() == null) { throw new NullPointerException("Attempt to add procedure with no name."); } // TODO: change method signature to return replaced procedure and make // sure the // Modeler handles it... Object existingProcedure = procedureMap.get(procedure.getName()); if (existingProcedure != null) { if (existingProcedure == procedure) { return; } else { throw new IllegalArgumentException("An attempt to override procedure '" + procedure.getName()); } } procedureMap.put(procedure.getName(), procedure); procedure.setDataMap(this); } public void removeProcedure(String name) { procedureMap.remove(name); } /** * Returns a sorted unmodifiable map of Procedures in this DataMap keyed by * name. */ public SortedMap<String, Procedure> getProcedureMap() { return Collections.unmodifiableSortedMap(procedureMap); } /** * Returns a parent namespace where this DataMap resides. Parent * EntityNamespace is used to establish relationships with entities in other * DataMaps. * * @since 1.1 */ public MappingNamespace getNamespace() { return namespace; } /** * Sets a parent namespace where this DataMap resides. Parent * EntityNamespace is used to establish relationships with entities in other * DataMaps. * * @since 1.1 */ public void setNamespace(MappingNamespace namespace) { this.namespace = namespace; } /** * @since 1.1 */ public int getDefaultLockType() { return defaultLockType; } /** * @since 1.1 */ public void setDefaultLockType(int defaultLockType) { this.defaultLockType = defaultLockType; } /** * @since 1.2 */ public boolean isClientSupported() { return clientSupported; } /** * @since 1.2 */ public void setClientSupported(boolean clientSupport) { this.clientSupported = clientSupport; } /** * Returns default client package. * * @since 1.2 */ public String getDefaultClientPackage() { return defaultClientPackage; } /** * @since 1.2 */ public void setDefaultClientPackage(String defaultClientPackage) { this.defaultClientPackage = defaultClientPackage; } /** * Returns default client superclass. * * @since 3.0 */ public String getDefaultClientSuperclass() { return defaultClientSuperclass; } /** * @since 3.0 */ public void setDefaultClientSuperclass(String defaultClientSuperclass) { this.defaultClientSuperclass = defaultClientSuperclass; } /** * @since 1.1 */ public String getDefaultPackage() { return defaultPackage; } /** * @since 1.1 */ public void setDefaultPackage(String defaultPackage) { this.defaultPackage = defaultPackage; } /** * @since 1.1 */ public String getDefaultSchema() { return defaultSchema; } /** * @since 1.1 */ public void setDefaultSchema(String defaultSchema) { this.defaultSchema = defaultSchema; } /** * @since 1.1 */ public String getDefaultSuperclass() { return defaultSuperclass; } /** * @since 1.1 */ public void setDefaultSuperclass(String defaultSuperclass) { this.defaultSuperclass = defaultSuperclass; } /** * DbEntity property changed. May be name, attribute or relationship added * or removed, etc. Attribute and relationship property changes are handled * in respective listeners. * * @since 1.2 */ public void dbEntityChanged(EntityEvent e) { Entity entity = e.getEntity(); if (entity instanceof DbEntity) { DbEntity dbEntity = (DbEntity) entity; dbEntity.dbEntityChanged(e); // finish up the name change here because we // do not have direct access to the dbEntityMap if (e.isNameChange()) { // remove the entity from the map with the old name dbEntityMap.remove(e.getOldName()); // add the entity back in with the new name dbEntityMap.put(e.getNewName(), dbEntity); // important - clear parent namespace: MappingNamespace ns = getNamespace(); if (ns instanceof EntityResolver) { ((EntityResolver) ns).refreshMappingCache(); } } } } /** New entity has been created/added. */ public void dbEntityAdded(EntityEvent e) { // does nothing currently } /** Entity has been removed. */ public void dbEntityRemoved(EntityEvent e) { // does nothing currently } /** * ObjEntity property changed. May be name, attribute or relationship added * or removed, etc. Attribute and relationship property changes are handled * in respective listeners. * * @since 1.2 */ public void objEntityChanged(EntityEvent e) { Entity entity = e.getEntity(); if (entity instanceof ObjEntity) { ObjEntity objEntity = (ObjEntity) entity; objEntity.objEntityChanged(e); // finish up the name change here because we // do not have direct access to the objEntityMap if (e.isNameChange()) { // remove the entity from the map with the old name objEntityMap.remove(e.getOldName()); // add the entity back in with the new name objEntityMap.put(e.getNewName(), objEntity); // important - clear parent namespace: MappingNamespace ns = getNamespace(); if (ns instanceof EntityResolver) { ((EntityResolver) ns).refreshMappingCache(); } } } } /** New entity has been created/added. */ public void objEntityAdded(EntityEvent e) { // does nothing currently } /** Entity has been removed. */ public void objEntityRemoved(EntityEvent e) { // does nothing currently } /** * @since 3.1 */ public Resource getConfigurationSource() { return configurationSource; } /** * @since 3.1 */ public void setConfigurationSource(Resource configurationSource) { this.configurationSource = configurationSource; } /** * @since 4.0 */ public String getDefaultCatalog() { return defaultCatalog; } /** * @since 4.0 */ public void setDefaultCatalog(String defaultCatalog) { this.defaultCatalog = defaultCatalog; } /** * @since 4.0 */ public EntityInheritanceTree getInheritanceTree(String entityName) { // TODO: we should support that throw new UnsupportedOperationException(); } /** * @since 4.0 */ public ObjEntity getObjEntity(Class<?> entityClass) { if (entityClass == null) { return null; } String className = entityClass.getName(); for (ObjEntity e : objEntityMap.values()) { if (className.equals(e.getClassName())) { return e; } } return null; } public ObjEntity getObjEntity(Persistent object) { ObjectId id = object.getObjectId(); if (id != null) { return getObjEntity(id.getEntityName()); } else { return getObjEntity(object.getClass()); } } public void clear() { clearDbEntities(); clearEmbeddables(); clearObjEntities(); clearProcedures(); clearQueries(); clearResultSets(); } /** * * @return package + "." + name when it is possible otherwise just name * * @since 4.0 */ public String getNameWithDefaultPackage(String name) { return getNameWithPackage(defaultPackage, name); } /** * * @return package + "." + name when it is possible otherwise just name * * @since 4.0 */ public static String getNameWithPackage(String pack, String name) { if (Util.isEmptyString(pack)) { return name; } else { return pack + (pack.endsWith(".") ? "" : ".") + name; } } /** * * @param name * @return package + "." + name when it is possible otherwise just name * * @since 4.0 */ public String getNameWithDefaultClientPackage(String name) { return getNameWithPackage(defaultClientPackage, name); } public Map<String, ObjEntity> getSubclassesForObjEntity(ObjEntity superEntity) { Map<String, ObjEntity> subObjectEntities = new HashMap<>(5); for (ObjEntity objectEntity : objEntityMap.values()) { if (superEntity.getName().equals(objectEntity.getSuperEntityName())) { subObjectEntities.put(objectEntity.getName(), objectEntity); } } return subObjectEntities; } }