blob: eea34364cf6f33a9e4b9ad4482101b2157545cd0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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.core.internal.resource.java;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jpt.core.JpaAnnotationProvider;
import org.eclipse.jpt.core.ResourceModelListener;
import org.eclipse.jpt.core.internal.utility.jdt.JDTTools;
import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType;
import org.eclipse.jpt.core.resource.java.JpaCompilationUnit;
import org.eclipse.jpt.core.utility.TextRange;
import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
import org.eclipse.jpt.utility.CommandExecutorProvider;
import org.eclipse.jpt.utility.internal.BitTools;
import org.eclipse.jpt.utility.internal.iterators.EmptyIterator;
/**
*
*/
public class JpaCompilationUnitImpl
extends AbstractJavaResourceNode
implements JpaCompilationUnit
{
private final ICompilationUnit compilationUnit;
private final JpaAnnotationProvider annotationProvider;
private final CommandExecutorProvider modifySharedDocumentCommandExecutorProvider;
private final AnnotationEditFormatter annotationEditFormatter;
private final ResourceModelListener resourceModelListener;
/**
* The primary type of the AST compilation unit. We are not going to handle
* multiple types defined in a single compilation unit. Entities must have
* a public/protected no-arg constructor, and there is no way to access
* the constructor in a package class (which is what all top-level,
* non-primary classes must be).
*/
protected JavaResourcePersistentType persistentType;
// ********** construction **********
public JpaCompilationUnitImpl(
ICompilationUnit compilationUnit,
JpaAnnotationProvider annotationProvider,
CommandExecutorProvider modifySharedDocumentCommandExecutorProvider,
AnnotationEditFormatter annotationEditFormatter,
ResourceModelListener resourceModelListener) {
super(null); // the JPA compilation unit is the root of its sub-tree
this.compilationUnit = compilationUnit;
this.annotationProvider = annotationProvider;
this.modifySharedDocumentCommandExecutorProvider = modifySharedDocumentCommandExecutorProvider;
this.annotationEditFormatter = annotationEditFormatter;
this.resourceModelListener = resourceModelListener;
this.persistentType = this.buildPersistentType();
}
protected JavaResourcePersistentType buildPersistentType() {
this.openCompilationUnit();
CompilationUnit astRoot = this.buildASTRoot();
this.closeCompilationUnit();
return this.buildPersistentType(astRoot);
}
protected void openCompilationUnit() {
try {
this.compilationUnit.open(null);
} catch (JavaModelException ex) {
// do nothing - we just won't have a primary type in this case
}
}
protected void closeCompilationUnit() {
try {
this.compilationUnit.close();
} catch (JavaModelException ex) {
// hmmm
}
}
protected JavaResourcePersistentType buildPersistentType(CompilationUnit astRoot) {
TypeDeclaration td = this.getPrimaryType(astRoot);
return (td == null) ? null : this.buildPersistentType(astRoot, td);
}
public void initialize(CompilationUnit astRoot) {
// never called?
}
// ********** AbstractJavaResourceNode overrides **********
@Override
protected boolean requiresParent() {
return false;
}
@Override
public JpaCompilationUnit getJpaCompilationUnit() {
return this;
}
@Override
public JpaAnnotationProvider getAnnotationProvider() {
return this.annotationProvider;
}
// ********** JavaResourceNode implementation **********
public void update(CompilationUnit astRoot) {
TypeDeclaration td = this.getPrimaryType(astRoot);
if (td == null) {
this.persistentType = null;
} else {
if (this.persistentType == null) {
this.persistentType = this.buildPersistentType(astRoot, td);
} else {
this.persistentType.update(astRoot);
}
}
}
public TextRange getTextRange(CompilationUnit astRoot) {
return null;
}
// ********** JpaCompilationUnit implementation **********
public ICompilationUnit getCompilationUnit() {
return this.compilationUnit;
}
public Iterator<JavaResourcePersistentType> persistableTypes() {
return (this.persistentType == null) ?
EmptyIterator.<JavaResourcePersistentType>instance() :
this.persistentType.allPersistableTypes();
}
public void resourceModelChanged() {
this.resourceModelListener.resourceModelChanged();
}
public void resolveTypes() {
if (this.persistentType != null) {
this.persistentType.resolveTypes(this.buildASTRoot());
}
}
public CommandExecutorProvider getModifySharedDocumentCommandExecutorProvider() {
return this.modifySharedDocumentCommandExecutorProvider;
}
public AnnotationEditFormatter getAnnotationEditFormatter() {
return this.annotationEditFormatter;
}
// ********** Java changes **********
public void javaElementChanged(ElementChangedEvent event) {
this.synchWithJavaDelta(event.getDelta());
}
protected void synchWithJavaDelta(IJavaElementDelta delta) {
switch (delta.getElement().getElementType()) {
case IJavaElement.JAVA_PROJECT :
if (this.classpathHasChanged(delta)) {
this.updateFromJava();
break; // no need to check further
}
case IJavaElement.JAVA_MODEL :
case IJavaElement.PACKAGE_FRAGMENT_ROOT :
case IJavaElement.PACKAGE_FRAGMENT :
this.synchChildrenWithJavaDelta(delta);
break;
case IJavaElement.COMPILATION_UNIT :
if (this.deltaIsRelevant(delta)) {
this.updateFromJava();
}
break;
default :
break; // the element type is somehow held by a compilation unit (i.e. probably doesn't happen)
}
}
protected void synchChildrenWithJavaDelta(IJavaElementDelta delta) {
for (IJavaElementDelta child : delta.getAffectedChildren()) {
this.synchWithJavaDelta(child); // recurse
}
}
// 235384 - We need to update all compilation units when a classpath change occurs.
// The persistence.jar could have been added to or removed from the
// classpath which affects whether we know about the JPA annotations.
protected boolean classpathHasChanged(IJavaElementDelta delta) {
return BitTools.anyFlagsAreSet(delta.getFlags(), this.getClasspathChangedFlags());
}
protected int getClasspathChangedFlags() {
return CLASSPATH_CHANGED_FLAGS;
}
protected static final int CLASSPATH_CHANGED_FLAGS =
IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED |
IJavaElementDelta.F_CLASSPATH_CHANGED;
protected boolean deltaIsRelevant(IJavaElementDelta delta) {
// ignore changes to/from primary working copy - no content has changed;
// and make sure there are no other flags set that indicate *both* a
// change to/from primary working copy *and* content has changed
if (BitTools.onlyFlagIsSet(delta.getFlags(), IJavaElementDelta.F_PRIMARY_WORKING_COPY)) {
return false;
}
// we get the java notification for removal before we get the resource notification;
// we do not need to handle this event and will get exceptions building an astRoot if we try
if (delta.getKind() == IJavaElementDelta.REMOVED) {
return false;
}
return delta.getElement().equals(this.compilationUnit);
}
protected void updateFromJava() {
this.update(this.buildASTRoot());
}
// ********** internal **********
protected CompilationUnit buildASTRoot() {
return JDTTools.buildASTRoot(this.compilationUnit);
}
protected JavaResourcePersistentType buildPersistentType(CompilationUnit astRoot, TypeDeclaration typeDeclaration) {
return JavaResourcePersistentTypeImpl.newInstance(this, typeDeclaration, astRoot);
}
/**
* i.e. the type with the same name as the compilation unit;
* return the first class or interface (ignore annotations and enums) with
* the same name as the compilation unit (file);
* NB: this type could be in error if there is an annotation or enum
* with the same name preceding it in the compilation unit
*
* Return null if resolveBinding() on the TypeDeclaration returns null
* This can occur if the project JRE is removed (bug 225332)
*/
protected TypeDeclaration getPrimaryType(CompilationUnit astRoot) {
String primaryTypeName = this.getPrimaryTypeName();
for (AbstractTypeDeclaration atd : types(astRoot)) {
if ((atd.getNodeType() == ASTNode.TYPE_DECLARATION)
&& atd.getName().getFullyQualifiedName().equals(primaryTypeName)) {
return (atd.resolveBinding()) != null ? (TypeDeclaration) atd : null;
}
}
return null;
}
// minimize scope of suppressed warnings
@SuppressWarnings("unchecked")
protected static List<AbstractTypeDeclaration> types(CompilationUnit astRoot) {
return astRoot.types();
}
/**
* i.e. the name of the compilation unit
*/
protected String getPrimaryTypeName() {
return removeJavaExtension(this.compilationUnit.getElementName());
}
protected static String removeJavaExtension(String fileName) {
int index = fileName.lastIndexOf(".java"); //$NON-NLS-1$
return (index == -1) ? fileName : fileName.substring(0, index);
}
@Override
public void toString(StringBuilder sb) {
sb.append(this.persistentType.getName());
}
}