blob: 4e24b4d1cd7153e85612d4a2c194bc8a18e913f2 [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.
*
* 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
* $Id: BaseImportChecker.java 23451 2010-02-04 20:33:32Z stephan $
*
* 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 java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression.DecapsulationState;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.AccessRule;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import base org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import base org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
/**
* This team checks whether base-imports are backed up by proper aspectBinding declarations.
* (Only activated if the project has the PluginNature).
* This team is only temporarily instantiated/activated by AdaptorActivator.JavaProject(JavaProject).
*
* This team also handles the forcedExport declarations from aspectBindings extensions
* and correspondingly converts some diagnostics from forbiddenAccess to decapsulationByForcedExport.
*
* Other parts involved:
*
* + PDEAdaptor is responsible for adding aspectBindingData (of type AdaptedBaseBundle)
* to ClasspathAccessRules and adjusting the problemID
*
* + org.eclipse.jdt.internal.core.builder.NameEnvironment
* - computeClasspathLocations(IWorkspaceRoot, JavaProject, SimpleLookupTable)
* Feed AccessRuleSet from ClasspathEntry into ClasspathLocations like ClasspathDirectory.
*
* + org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment
* - setAccessRestriction(ReferenceBinding, AccessRestriction)
* - AccessRestriction getAccessRestriction(TypeBinding type)
* Pass AccessRestriction from sources like ClasspathDirectory.accessRuleSet into the compiler.
*
* @author stephan
* @since 1.1.2
*/
@SuppressWarnings("restriction")
public team class BaseImportChecker extends CompilationThreadWatcher
{
private AspectBindingReader aspectBindingReader;
public BaseImportChecker() {/* emtpy ctor for OT/Equinox */ }
/**
* @param aspectBindingReader must be non-null (and initialized).
*/
public BaseImportChecker(AspectBindingReader aspectBindingReader) {
this.aspectBindingReader= aspectBindingReader;
}
/**
* If a forced exports exist convert some diagnostics from forbiddenAccess
* to decapsulationByForcedExport.
*/
protected class ProblemReporter playedBy ProblemReporter
{
// imports via callout:
protected void baseImportInRegularClass(TypeDeclaration firstType, ImportReference reference)
-> void baseImportInRegularClass(TypeDeclaration firstType, ImportReference reference);
protected void illegalBaseImportNoAspectBinding(ImportReference ref, String teamName)
-> void illegalBaseImportNoAspectBinding(ImportReference ref, String teamName);
protected void illegalBaseImport(ImportReference ref, String expectedBasePlugin, String actualBasePlugin)
-> void illegalBaseImport(ImportReference ref, String expectedBasePlugin, String actualBasePlugin);
void illegalUseOfForcedExport(ReferenceBinding type, ASTNode reference)
-> void illegalUseOfForcedExport(ReferenceBinding type, ASTNode reference);
void decapsulationByForcedExport(ReferenceBinding type, ASTNode reference)
-> void decapsulationByForcedExport(ReferenceBinding type, ASTNode reference);
void baseImportFromSplitPackage(ImportReference ref, String expectedPlugin)
-> void baseImportFromSplitPackage(ImportReference ref, String expectedPlugin);
ReferenceContext getReferenceContext() -> get ReferenceContext referenceContext;
/** The callin entry into this role: analyze and report various access situations. */
@SuppressWarnings("basecall")
callin void forbiddenReference(TypeBinding type, ASTNode location, AccessRestriction restriction)
{
switch (restriction.getProblemId()) {
case IProblem.BaseclassDecapsulationForcedExport:
switch (getBaseclassDecapsulation(location)) {
case ALLOWED:
decapsulationByForcedExport((ReferenceBinding)type, location);
break;
case REPORTED:
break;
default:
// no forced export for regular use!
illegalUseOfForcedExport((ReferenceBinding)type, location);
}
break;
case IProblem.AdaptedPluginAccess:
// not a real error but requires consistency check against aspectBinding:
if (location instanceof ImportReference) {
ImportReference imp= (ImportReference)location;
if (imp.isBase()) {
String teamName= getReferenceTeam();
if (teamName == null)
baseImportInRegularClass(getPublicType(), imp);
Set<String> basePlugins= aspectBindingReader.getBasePlugins(teamName);
if (basePlugins == null || basePlugins.isEmpty()) {
illegalBaseImportNoAspectBinding(imp, teamName);
return;
}
String baseString = flattenSet(basePlugins);
Set<String> actualBases = new HashSet<String>();
AccessRule rule= restriction.getAccessRule();
if (rule.aspectBindingData != null) {
for (Object data : rule.aspectBindingData) {
AdaptedBaseBundle info= (AdaptedBaseBundle) data;
if (info.isAdaptedBy(teamName)) {
// OK, no error
if (info.hasPackageSplit)
baseImportFromSplitPackage(imp, baseString); // just a warning
return;
}
actualBases.add(info.getSymbolicName());
}
}
illegalBaseImport(imp, baseString, flattenSet(actualBases));
}
}
break;
default:
base.forbiddenReference(type, location, restriction);
}
}
void forbiddenReference(TypeBinding type, ASTNode location, AccessRestriction restriction)
<- replace void forbiddenReference(TypeBinding type, ASTNode location, byte entryType, AccessRestriction restriction)
with { type <- type, location <- location, restriction <- restriction }
void forbiddenReference(TypeBinding type, ASTNode location, AccessRestriction restriction)
<- replace void forbiddenReference(MethodBinding method, ASTNode location, byte entryType, AccessRestriction restriction)
with { type <- method.declaringClass, location <- location, restriction <- restriction }
void forbiddenReference(TypeBinding type, ASTNode location, AccessRestriction restriction)
<- replace void forbiddenReference(FieldBinding field, ASTNode location, byte entryType, AccessRestriction restriction)
with { type <- field.declaringClass, location <- location, restriction <- restriction }
private DecapsulationState getBaseclassDecapsulation(ASTNode location) {
if (location instanceof Expression) {
if (location instanceof AllocationExpression)
return DecapsulationState.REPORTED; // base-ctor expression.
if (location instanceof MessageSend)
return DecapsulationState.REPORTED; // callout message send.
Expression expr= (Expression) location;
DecapsulationState result= expr.getBaseclassDecapsulation();
if (result == DecapsulationState.ALLOWED)
expr.tagReportedBaseclassDecapsulation();
return result;
}
if (location instanceof ImportReference) {
ImportReference impRef= (ImportReference)location;
if (impRef.isBase())
return DecapsulationState.ALLOWED; // always need to report
}
return DecapsulationState.NONE;
}
private String getReferenceTeam() {
TypeDeclaration type= getPublicType();
if (type != null && type.isTeam())
return new String(type.binding.readableName());
return null;
}
private TypeDeclaration getPublicType() {
ReferenceContext context= getReferenceContext();
if (context instanceof CompilationUnitDeclaration) {
CompilationUnitDeclaration unit= (CompilationUnitDeclaration)context;
if (unit.types == null) return null;
for (TypeDeclaration type : unit.types)
if (Flags.isPublic(type.modifiers))
return type;
}
return null;
}
}
protected class ImportTracker playedBy CompilationUnitScope
{
SourceTypeBinding[] getTopLevelTypes() -> get SourceTypeBinding[] topLevelTypes;
private // don't publically expose protected role ProblemReporter
ProblemReporter problemReporter() -> ProblemReporter problemReporter();
/** When setting the base imports to a CUScope, check for imports from undeclared plug-ins. */
void setBaseImports(ImportBinding[] resolvedBaseImports, int baseCount, ImportReference[] refs)
<- before void setBaseImports(ImportBinding[] resolvedBaseImports, int baseCount, ImportReference[] refs);
void setBaseImports(ImportBinding[] resolvedBaseImports, int baseCount, ImportReference[] refs)
{
if (baseCount == 0) return;
ReferenceBinding teamType = findMainType();
String teamName= (teamType != null)
? new String(teamType.readableName()) : null;
for (int i=0; i<baseCount; i++) {
if (teamType == null) {
problemReporter().baseImportInRegularClass(null, refs[i]);
continue;
}
if (resolvedBaseImports[i].onDemand) // syntactically impossible
throw new InternalCompilerError("Ondemand base import not supported"); //$NON-NLS-1$
String basePlugins= null;
if (resolvedBaseImports[i].resolvedImport instanceof ReferenceBinding) {
ReferenceBinding importedType= (ReferenceBinding)resolvedBaseImports[i].resolvedImport;
if (!importedType.isValidBinding())
continue; // already reported
if (importedType.hasRestrictedAccess())
continue; // checked by forbiddenAccess()
if (aspectBindingReader.isAdaptingSelf(teamName)) {
char[][] current= CharOperation.splitOn('/', teamType.getFileName());
char[][] imported= CharOperation.splitOn('/', importedType.getFileName());
if (CharOperation.equals(current[1], imported[1]))
return;
basePlugins= "<self>"; //$NON-NLS-1$
}
}
if (basePlugins == null)
basePlugins= flattenSet(aspectBindingReader.getBasePlugins(teamName));
if (basePlugins != null)
problemReporter().illegalBaseImport(refs[i], basePlugins, null);
else
problemReporter().illegalBaseImportNoAspectBinding(refs[i], teamName);
}
}
private ReferenceBinding findMainType() {
SourceTypeBinding[] toplevelTypes = getTopLevelTypes();
if (toplevelTypes != null)
for (SourceTypeBinding referenceBinding : toplevelTypes)
if (referenceBinding.isPublic())
return referenceBinding;
return null;
}
}
@SuppressWarnings("nls")
String flattenSet(Set<String> stringSet) {
if (stringSet == null) return null;
Iterator<String> iterator = stringSet.iterator();
if (stringSet.size()==1) {
return iterator.next();
} else {
String result = "[";
while(true) {
result += iterator.next();
if (!iterator.hasNext()) break;
result += ", ";
}
return result + "]";
}
}
}