blob: da9bde62a7dee44336a1e3b75cf81da497ac931f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2013 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.core.internal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jpt.common.core.internal.utility.PlatformTools;
import org.eclipse.jpt.common.utility.internal.ObjectTools;
import org.eclipse.jpt.common.utility.internal.model.AbstractModel;
import org.eclipse.jpt.common.utility.internal.model.AspectChangeSupport;
import org.eclipse.jpt.common.utility.internal.model.ChangeSupport;
import org.eclipse.jpt.jpa.core.JpaDataSource;
import org.eclipse.jpt.jpa.core.JpaFactory;
import org.eclipse.jpt.jpa.core.JpaFile;
import org.eclipse.jpt.jpa.core.JpaModel;
import org.eclipse.jpt.jpa.core.JpaPlatform;
import org.eclipse.jpt.jpa.core.JpaPlatformVariation;
import org.eclipse.jpt.jpa.core.JpaProject;
import org.eclipse.jpt.jpa.core.jpa2.JpaFactory2_0;
import org.eclipse.jpt.jpa.core.jpa2.JpaProject2_0;
import org.eclipse.jpt.jpa.core.jpa2_1.JpaFactory2_1;
import org.eclipse.jpt.jpa.core.jpa2_1.JpaProject2_1;
import org.eclipse.jpt.jpa.db.Catalog;
import org.eclipse.jpt.jpa.db.Database;
/**
* Some common Dali behavior:<ul>
* <li>containment hierarchy
* <li>Eclipse adaptable
* <li>update triggers
* </ul>
*/
public abstract class AbstractJpaModel<P extends JpaModel>
extends AbstractModel
implements JpaModel
{
protected final P parent;
// ********** constructor/initialization **********
protected AbstractJpaModel(P parent) {
super();
this.checkParent(parent);
this.parent = parent;
}
protected void checkParent(JpaModel p) {
if (p == null) {
if (this.requiresParent()) {
throw new IllegalArgumentException("'parent' cannot be null"); //$NON-NLS-1$
}
} else {
if (this.forbidsParent()) {
throw new IllegalArgumentException("'parent' must be null"); //$NON-NLS-1$
}
}
}
protected boolean requiresParent() {
return true;
}
protected final boolean forbidsParent() {
return ! this.requiresParent(); // assume 'parent' is not optional
}
@Override
protected ChangeSupport buildChangeSupport() {
return new AspectChangeSupport(this, this.buildChangeSupportListener());
}
protected AspectChangeSupport.Listener buildChangeSupportListener() {
return new AspectChangeSupportListener();
}
public class AspectChangeSupportListener
implements AspectChangeSupport.Listener
{
public void aspectChanged(String aspectName) {
AbstractJpaModel.this.aspectChanged(aspectName);
}
@Override
public String toString() {
return ObjectTools.toString(this);
}
}
// ********** IAdaptable implementation **********
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object getAdapter(Class adapter) {
return PlatformTools.getAdapter(this, adapter);
}
// ********** JpaModel implementation **********
public P getParent() {
return this.parent;
}
/**
* Overridden in:<ul>
* <li>{@link AbstractJpaProject#getResource() AbstractJpaProject}
* <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.GenericContextRoot#getResource() GenericContextModelRoot}
* <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.java.GenericJarFile#getResource() GenericJarFile}
* <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.orm.GenericOrmXml#getResource() GenericOrmXml}
* <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.persistence.GenericPersistenceXml#getResource() GenericPersistenceXml}
* <li>{@link org.eclipse.jpt.jpa.core.internal.context.java.AbstractJavaManagedType#getResource() AbstractJavaManagedType}
* </ul>
*/
public IResource getResource() {
return this.parent.getResource();
}
/**
* Overridden in:<ul>
* <li>{@link AbstractJpaProject#getJpaProject() AbstractJpaProject}
* </ul>
*/
public JpaProject getJpaProject() {
return this.parent.getJpaProject();
}
public JpaProject.Manager getJpaProjectManager() {
return this.getJpaProject().getManager();
}
public JpaPlatform getJpaPlatform() {
return this.getJpaProject().getJpaPlatform();
}
// ********** convenience methods **********
protected IJavaProject getJavaProject() {
return this.getJpaProject().getJavaProject();
}
protected JpaPlatform.Version getJpaPlatformVersion() {
return this.getJpaPlatform().getJpaVersion();
}
protected boolean isJpa2_0Compatible() {
return this.getJpaPlatformVersion().isCompatibleWithJpaVersion(JpaProject2_0.FACET_VERSION_STRING);
}
protected boolean isJpa2_1Compatible() {
return this.getJpaPlatformVersion().isCompatibleWithJpaVersion(JpaProject2_1.FACET_VERSION_STRING);
}
/**
* Call {@link #isJpa2_0Compatible()} before calling this method.
*/
protected JpaFactory2_0 getJpaFactory2_0() {
return (JpaFactory2_0) this.getJpaFactory();
}
/**
* Call {@link #isJpa2_1Compatible()} before calling this method.
*/
protected JpaFactory2_1 getJpaFactory2_1() {
return (JpaFactory2_1) this.getJpaFactory();
}
protected JpaFactory getJpaFactory() {
return this.getJpaPlatform().getJpaFactory();
}
protected JpaPlatformVariation getJpaPlatformVariation() {
return this.getJpaPlatform().getJpaVariation();
}
protected JpaFile getJpaFile(IFile file) {
return this.getJpaProject().getJpaFile(file);
}
protected JpaDataSource getDataSource() {
return this.getJpaProject().getDataSource();
}
protected Database getDatabase() {
return this.getDataSource().getDatabase();
}
protected boolean connectionProfileIsActive() {
return this.getDataSource().connectionProfileIsActive();
}
/**
* Pre-condition: specified catalog <em>identifier</em> is not
* <code>null</code>.
* <p>
* <strong>NB:</strong> Do not use the catalog <em>name</em>.
*/
protected Catalog resolveDbCatalog(String catalog) {
Database database = this.getDatabase();
return (database == null) ? null : database.getCatalogForIdentifier(catalog);
}
// ********** AspectChangeSupport.Listener support **********
/**
* The specified aspect name will be <code>null</code> for <em>state</em>
* changes.
*/
protected void aspectChanged(String aspectName) {
if (this.aspectTriggersUpdate(aspectName)) {
// String msg = Thread.currentThread() + " aspect change: " + this + ": " + aspectName;
// System.out.println(msg);
// new Exception(msg).printStackTrace(System.out);
this.stateChanged();
}
}
/**
* The specified aspect name will be <code>null</code> for <em>state</em>
* changes.
*/
protected boolean aspectTriggersUpdate(String aspectName) {
return ! this.aspectDoesNotTriggerUpdate(aspectName);
}
/**
* The specified aspect name will be <code>null</code> for <em>state</em>
* changes.
*/
protected boolean aspectDoesNotTriggerUpdate(String aspectName) {
// ignore state changes so we don't get a stack overflow :-)
// (and we don't use state changes except here)
return (aspectName == null) ||
this.nonUpdateAspectNames().contains(aspectName);
}
protected final Set<String> nonUpdateAspectNames() {
@SuppressWarnings("unchecked")
Class<? extends AbstractJpaModel<?>> thisClass = (Class<? extends AbstractJpaModel<?>>) this.getClass();
synchronized (NON_UPDATE_ASPECT_NAME_SETS) {
HashSet<String> nonUpdateAspectNames = NON_UPDATE_ASPECT_NAME_SETS.get(thisClass);
if (nonUpdateAspectNames == null) {
nonUpdateAspectNames = new HashSet<String>();
this.addNonUpdateAspectNamesTo(nonUpdateAspectNames);
NON_UPDATE_ASPECT_NAME_SETS.put(thisClass, nonUpdateAspectNames);
}
return nonUpdateAspectNames;
}
}
/**
* Map sub-classes to the names of the aspects that should <em>not</em>
* trigger a {@link AbstractJpaProject#update() JPA project update}.
* This map is populated on-demand (i.e. as changes occur to instances of
* the sub-classes).
*/
private static final HashMap<Class<? extends AbstractJpaModel<?>>, HashSet<String>> NON_UPDATE_ASPECT_NAME_SETS = new HashMap<Class<? extends AbstractJpaModel<?>>, HashSet<String>>();
/**
* Extend this method to add (to the specified collection) the names of
* aspects that should <em>not</em> trigger a JPA project <em>update</em>.
* This list will be effective for <em>all</em> instances of the model's
* class and will be built only once (per class loader).
* <p>
* To disable the JPA project <em>update</em> for changes to a particular
* <em>instance</em>, override {@link #aspectTriggersUpdate(String)}.
*/
protected void addNonUpdateAspectNamesTo(@SuppressWarnings("unused") Set<String> nonUpdateAspectNames) {
// when you override this method, don't forget to include:
// super.addNonUpdateAspectNamesTo(nonUpdateAspectNames);
}
/**
* @see #aspectChanged(String)
* @see AbstractJpaProject#stateChanged()
* @see org.eclipse.jpt.jpa.core.internal.jpa1.context.GenericContextRoot#stateChanged()
*/
public void stateChanged() {
this.fireStateChanged();
if (this.parent != null) {
this.parent.stateChanged();
}
}
}