blob: 46618b31bd2fa261a1c2d015f4d4584f68a65a97 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2007 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.compiler.adaptor;
import static org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.eclipse.core.internal.resources.Project;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.SourceElementParser;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
import org.eclipse.jdt.internal.core.ClasspathAccessRule;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.core.compiler.OTNameUtils;
import org.eclipse.pde.internal.core.RequiredPluginsClasspathContainer;
import org.objectteams.LiftingVetoException;
import org.objectteams.Team;
import base org.eclipse.jdt.core.JavaCore;
import base org.eclipse.jdt.internal.core.CompilationUnitProblemFinder;
import base org.eclipse.jdt.internal.core.JavaProject;
import base org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder;
import base org.eclipse.jdt.internal.core.builder.JavaBuilder;
import base org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance.CopyInheritance;
/**
* This team is activated via the OT/Equinox transformer plugin.
* It controls the activation of the BuildManager and BaseImportChecker teams.
*
* Control entry for BuildManager: IncrementalImageBuilder.
* Control entries for BaseImportChecker: JavaBuilder and CompilationUnitProblemFinder
* @author stephan
*/
@SuppressWarnings("restriction")
public team class AdaptorActivator
{
/**
* Name of classpath attributes denoting the base bundle from which a classpath entry originates.
* Persistently stored so we can later associate aspect binding data to that classpath entry.
*/
public static final String CPENTRY_ATTR_ORIGIN_BASE_BUNDLE = "org.eclipse.objectteams.originBaseBundle"; //$NON-NLS-1$
/**
* Copy of a constant from PDECore to reduce dependencies (must be compared with .equals())).
*/
public static final IPath REQUIRED_PLUGINS_CONTAINER_PATH = new Path("org.eclipse.pde.core.requiredPlugins"); //$NON-NLS-1$
public static AdaptorActivator instance;
public AdaptorActivator() {
instance= this;
}
/**
* Pass the AspectBindingReader from a JavaProject to its RequiredPluginsClasspathContainer,
* and pass aspect binding data from the reader to individual classpath entries having a
* originBaseBundle attribute.
*/
@SuppressWarnings("decapsulation") // base class is final
protected class JavaCore playedBy JavaCore
{
IClasspathAttribute newClasspathAttribute(String name, String value) -> IClasspathAttribute newClasspathAttribute(String name, String value);
// (see TODO above) Attention:
// this solution would require partial array lifting if an element throws LiftingVetoException:
// void setClasspathContainer(OTEquinoxProject[] javaProjects, IClasspathContainer[] container)
void setClasspathContainer(IJavaProject[] javaProjects, IClasspathContainer[] containers)
<- before void setClasspathContainer(IPath container, IJavaProject[] affectedProjects, IClasspathContainer[] respectiveContainers, IProgressMonitor pm)
with { javaProjects <- affectedProjects,
containers <- respectiveContainers
}
// static void setClasspathContainer(OTEquinoxProject[] javaProject, IClasspathContainer[] container)
static void setClasspathContainer(IJavaProject[] javaProjects, IClasspathContainer[] containers)
{
try {
for (int i=0; i<javaProjects.length; i++) {
Project project= (Project)javaProjects[i].getProject();
if ( containers[i] instanceof RequiredPluginsClasspathContainer // checking the name is not enough, could still be a UpdatedClasspathContainer
&& ProjectUtil.isOTPluginProject(project)) // avoid LiftingVetoException
{
AspectBindingReader aspectBindingReader = ResourceProjectAdaptor.getDefault().getAspectBindingReader(project);
if (PDEAdaptor.instance != null)
//otherwise PDEAdaptor has not been activated yet (PDE neither) hoping this can be ignored.
PDEAdaptor.instance.setAspectBindingReader(aspectBindingReader, (RequiredPluginsClasspathContainer)containers[i]);
else
org.eclipse.jdt.core.JavaCore.getJavaCore().getLog().log(new Status(Status.WARNING, "org.eclipse.objectteams.otdt.internal.compiler.adaptor", //$NON-NLS-1$
"PDEAdaptor not yet initialized while setting classpath for "+project.getName())); //$NON-NLS-1$
}
}
} catch (LiftingVetoException lve) {
// ignore, aspect just didn't apply
} catch (Throwable t) {
org.eclipse.jdt.core.JavaCore.getJavaCore().getLog().log(new Status(Status.ERROR, "org.eclipse.objectteams.otdt.internal.compiler.adaptor", "Error initializing AspectBindingReader", t)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
void addBaseBundleAttribute(IAccessRule[] accessRules, IClasspathAttribute[] extraAttributes)
<- replace IClasspathEntry newLibraryEntry(IPath path1, IPath path2, IPath path3, IAccessRule[] accessRules, IClasspathAttribute[] extraAttributes, boolean isExported)
when (accessRules != null && accessRules.length > 0)
with { accessRules <- accessRules, extraAttributes <- extraAttributes }
void addBaseBundleAttribute(IAccessRule[] accessRules, IClasspathAttribute[] extraAttributes)
<- replace IClasspathEntry newProjectEntry(IPath path, IAccessRule[] accessRules, boolean combineAccessRules, IClasspathAttribute[] extraAttributes, boolean isExported)
when (accessRules != null && accessRules.length > 0)
with { accessRules <- accessRules, extraAttributes <- extraAttributes }
/**
* when creating a new classpath entry for a require plugin, check whether this plugin is affected by aspect bindings.
* If so, add a classpath attribute to denote the base plugin from which this entry originates.
* If a package is split among several plugins, a rule may carry one aspectBindingData for each part of the split.
*/
static callin void addBaseBundleAttribute(IAccessRule[] accessRules, IClasspathAttribute[] extraAttributes) {
if (accessRules[0] instanceof ClasspathAccessRule) {
int len = extraAttributes.length;
int count = 0;
// filter existing attributes:
for (int i = 0; i < len; i++) {
if (!extraAttributes[i].getName().startsWith(CPENTRY_ATTR_ORIGIN_BASE_BUNDLE)) {
if (count != i)
extraAttributes[count] = extraAttributes[i];
count++;
}
}
// add new extraAttribute(s):
Object[] datas = ((ClasspathAccessRule)accessRules[0]).aspectBindingData;
int i = 0;
if (datas != null)
for (Object data : datas)
if (data instanceof AdaptedBaseBundle) {
// note: more than one data is rather infrequent, no need to optimize array copying
int idx = 0;
if (count < len) {
idx = count++;
} else {
System.arraycopy(extraAttributes, 0, extraAttributes=new IClasspathAttribute[len+1], 1, len);
}
String baseBundleName = ((AdaptedBaseBundle) data).getSymbolicName();
extraAttributes[idx] = newClasspathAttribute(CPENTRY_ATTR_ORIGIN_BASE_BUNDLE+(i++), baseBundleName);
}
// compact the array, if existing extraAttributes are being removed:
if (count < len)
System.arraycopy(extraAttributes, 0, extraAttributes=new IClasspathAttribute[count], 0, count);
}
base.addBaseBundleAttribute(accessRules, extraAttributes);
}
}
/**
* When a JavaProject computes its package fragment roots from its classpath entries,
* enhance the classpath entries with aspect binding data, using the classpath attribute
* to determine the corresponding base bundle.
*
* @author stephan
* @since 1.2.5
*/
protected class CPEntryEnhancer playedBy JavaProject {
IProject getProject() -> IProject getProject();
@SuppressWarnings("rawtypes")
void enhanceCPEntries(IClasspathEntry[] resolvedEntries)
<- before void computePackageFragmentRoots(IClasspathEntry[] resolvedEntries, ObjectVector accumulatedRoots,
HashSet rootIDs, IClasspathEntry referringEntry,
boolean retrieveExportedRoots, boolean filterModuleRoots,
Map rootToResolvedEntries, boolean excludeTestCode);
/**
* @param entries the entries whose access rules to enhance
* @throws LiftingVetoException when the project is not an OT/Equinox project.
*/
void enhanceCPEntries(IClasspathEntry[] entries) throws LiftingVetoException
{
IProject project = getProject();
AspectBindingReader reader = ResourceProjectAdaptor.getDefault().getAspectBindingReader((Project)project);
for (IClasspathEntry entry : entries) {
IClasspathAttribute[] attributes = entry.getExtraAttributes();
attributes_loop:
for (IClasspathAttribute attribute : attributes) {
if (attribute.getName().startsWith(CPENTRY_ATTR_ORIGIN_BASE_BUNDLE)) {
AdaptedBaseBundle aspectBindingData = reader.getAdaptationInfo(attribute.getValue());
if (aspectBindingData == null) continue; // means reader and attr are inconsistent
rules_loop:
for (IAccessRule rule : entry.getAccessRules())
if (rule instanceof ClasspathAccessRule)
if (!PDEAdaptor.addAspectBindingData((ClasspathAccessRule)rule, aspectBindingData))
break rules_loop; // when not adding assume all rules share the same aspect data
break attributes_loop;
}
}
}
}
}
/**
* Interface to the controlling builder. Tasks:
* <ul>
* <li>Life-cycle management for the BuildManager</li>
* <li>cflow like bracket [initializeBuilder,cleanup]
* for temporary activation of BaseImportChecker.
* Note, that build() is not a suitable join point,
* because javaProject is not yes assigned.</li>
* </ul>
*/
protected class JavaBuilderObserver playedBy JavaBuilder
{
@SuppressWarnings("decapsulation")
Project getProject() -> get JavaProject javaProject
with { result <- (Project)javaProject.getProject() }
Team projectWatcher= null;
// initialize local data structures when a full build is started:
void initialize(int kind) <- after int initializeBuilder(int kind, boolean forBuild);
void initialize(int kind) {
if (kind == FULL_BUILD)
manager.initializeDependencyStorage();
try {
this.projectWatcher= ResourceProjectAdaptor.getDefault().getChecker(getProject());
} catch (LiftingVetoException lve) {
this.projectWatcher= new PlainProjectWatcher();
}
this.projectWatcher.activate();
}
cleanup <- after cleanup;
void cleanup() {
if (this.projectWatcher != null)
this.projectWatcher.deactivate();
this.projectWatcher= null;
}
// need to recompute the classpath if aspect binding data have changed:
@SuppressWarnings("decapsulation")
boolean aspectBindingHasChanged() <- replace boolean hasClasspathChanged();
@SuppressWarnings("basecall")
callin boolean aspectBindingHasChanged() {
try {
if (ResourceProjectAdaptor.getDefault().hasAspectDataChanged(getProject()))
return true;
} catch (LiftingVetoException lve) {
// thrown while lifting project, means that javaProject is not OT-Project.
}
return base.aspectBindingHasChanged();
}
}
/**
* This role observes another entry into the compiler to activate a BaseImportChecker if needed.
*/
protected class CompilationUnitProblemFinder playedBy CompilationUnitProblemFinder
{
@SuppressWarnings("rawtypes")
void activateChecker(ICompilationUnit unitElement)
<- replace CompilationUnitDeclaration process(CompilationUnit unitElement,
SourceElementParser parser,
WorkingCopyOwner workingCopyOwner,
HashMap problems,
boolean creatingAST,
int reconcileFlags,
IProgressMonitor monitor)
with { unitElement <- unitElement }
static callin void activateChecker(ICompilationUnit unitElement)
throws JavaModelException
{
within (getChecker(unitElement))
base.activateChecker(unitElement);
}
static Team getChecker(ICompilationUnit unitElement) {
try {
IProject project= ProjectUtil.safeGetOTPluginProject(unitElement);
if (project != null) {
Team baseChecker= ResourceProjectAdaptor.getDefault().getChecker(project);
if (baseChecker != null)
return baseChecker;
}
} catch (LiftingVetoException lve) {
// shouldn't happen, have checked above.
}
return new PlainProjectWatcher(); // fallback for non OT-Plugin projects
}
}
private BuildManager manager = new BuildManager();
/**
* This role observes the IncrementalImageBuilder.
* It enables all callins of BuildManager only while
* performing an incremental build (method build(deltas)).
*/
protected class BuilderGuard playedBy IncrementalImageBuilder
{
build <- replace build;
callin boolean build(SimpleLookupTable deltas)
{
// Activation only for this thread/control flow:
within (manager.start()) {
return base.build(deltas);
}
}
}
/**
* This role class simply tracks all executions of CopyInheritance.copyRole(..)
*
* Purpose: collect data for recompiling sub-teams if tsuper roles have been changed.
*/
protected class CopyInheritanceObserver playedBy CopyInheritance
{
// This trigger applies to source and binary tsupers being copied into source:
void observeCopyRole(ReferenceBinding superRole, char[] subTeamFileName)
<- after TypeDeclaration copyRole(ReferenceBinding tsuperRole,
boolean isNestedType,
TypeDeclaration subTeamDecl,
boolean isTsuperTeam)
base when (result != null)
with { superRole <- tsuperRole,
subTeamFileName <- subTeamDecl.compilationResult.getFileName() }
// static for optimization: avoid lifting.
static void observeCopyRole(ReferenceBinding superRole, char[] subTeamFileName)
{
if (superRole.enclosingType().id == IOTConstants.T_OrgObjectTeamsTeam)
return;
if (subTeamFileName == null || subTeamFileName[0] != '/')
return; // only useful if an absolute path is given.
// no need to recompile these:
char[] superRoleName = superRole.internalName();
if ( BuildManager.isPredefinedRole(superRoleName)
|| OTNameUtils.isTSuperMarkerInterface(superRoleName))
return;
AdaptorActivator.this.manager.recordCopiedRole(superRole.attributeName(), subTeamFileName);
}
@SuppressWarnings("decapsulation")
boolean shouldPreserveBinary(ReferenceBinding role, CompilationResult cResult)
<-replace boolean shouldPreserveBinaryRole(ReferenceBinding role, CompilationResult cResult);
static callin boolean shouldPreserveBinary(ReferenceBinding role, CompilationResult cResult)
{
if (!base.shouldPreserveBinary(role, cResult))
return false;
if (!AdaptorActivator.this.manager.isActive())
return true;
return AdaptorActivator.this.manager.shouldPreserveBinaryRole(role, cResult);
}
}
}