blob: 95af2be1077a05c6bdeca1cf22a716daa1ef8943 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2009, 2019 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
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.pde.validation;
import static org.eclipse.objectteams.otequinox.Constants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IMethodMappingBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.objectteams.otdt.core.IMethodMapping;
import org.eclipse.objectteams.otdt.core.IRoleType;
import org.eclipse.objectteams.otdt.core.OTModelManager;
import org.eclipse.objectteams.otdt.internal.pde.ui.OTPDEUIMessages;
import org.eclipse.objectteams.otequinox.ActivationKind;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.internal.core.builders.CompilerFlags;
import org.eclipse.pde.internal.core.builders.IHeader;
import org.eclipse.pde.internal.core.builders.IncrementalErrorReporter.VirtualMarker;
import org.eclipse.pde.internal.core.builders.PDEMarkerFactory;
import org.eclipse.pde.internal.core.ibundle.IManifestHeader;
import org.eclipse.pde.internal.core.text.bundle.BundleActivationPolicyHeader;
import org.eclipse.pde.internal.core.text.bundle.BundleModel;
import org.eclipse.pde.internal.core.text.plugin.PluginAttribute;
import org.eclipse.pde.internal.ui.correction.AbstractManifestMarkerResolution;
import org.eclipse.pde.internal.ui.correction.AbstractPDEMarkerResolution;
import org.eclipse.pde.internal.ui.correction.AbstractXMLMarkerResolution;
import org.eclipse.pde.internal.ui.correction.AddExportPackageMarkerResolution;
import org.eclipse.ui.IMarkerResolution;
import org.osgi.framework.Constants;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.resource.Capability;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import base org.eclipse.pde.internal.core.builders.BundleErrorReporter;
import base org.eclipse.pde.internal.core.builders.ExtensionsErrorReporter;
import base org.eclipse.pde.internal.core.builders.ManifestConsistencyChecker;
import base org.eclipse.pde.internal.core.builders.XMLErrorReporter;
import base org.eclipse.pde.internal.ui.correction.ResolutionGenerator;
/**
* Enhanced validation of bundle manifests.
* <ul>
* <li>Check whether all bundles with aspectBindings have a proper activation policy set,<br>
* Provide suitable quick assist if activation policy is wrong/missing.</li>
* </ul>
*
* @author stephan
* @since 1.2.7
*/
@SuppressWarnings("restriction")
public team class BundleValidation
{
/** Constant for a problem that can be resolved by adding an activation policy to the manifest. */
static final int ADD_ACTIVATION_POLICY = 0x1801; // must not overlap with any constant in org.eclipse.pde.internal.core.builders.PDEMarkerFactory.
/** Constant for a problem that can be resolved by adding an activation policy to the manifest. */
static final int ADD_PACKAGE_EXPORT = 0x1802; // must not overlap with any constant in org.eclipse.pde.internal.core.builders.PDEMarkerFactory.
static final int CHANGE_DOT_TO_DOLLAR = 0x1803; // must not overlap with any constant in org.eclipse.pde.internal.core.builders.PDEMarkerFactory.
ThreadLocal<BundleCheckingContext> bundleContext = new ThreadLocal<BundleCheckingContext>();
/**
* Defines the context of validating one bundle.
* One instance of this role exists per control flow (= per thread)
* for retrieval in downstream callin bindings.
*/
protected class BundleCheckingContext playedBy ManifestConsistencyChecker
{
// flags set during validation of one bundle:
protected boolean isAspectBundle = false;
protected boolean hasTeamActivation = false;
protected Set<String> aspectPackages = new HashSet<String>();
/** packages containing bound base classes, which require the team to be bound to the corresponding base bundle. */
public Map<String,List<String>> requiredBasePackagesPerTeam = new HashMap<String, List<String>>();
IProject getProject() -> IProject getProject();
@SuppressWarnings("decapsulation")
spanContext <- replace validateFiles;
callin void spanContext() {
BundleValidation.this.bundleContext.set(this);
try {
base.spanContext();
} finally {
// withdraw role, is for one-time use only:
BundleValidation.this.bundleContext.set(null);
BundleValidation.this.unregisterRole(this, BundleCheckingContext.class);
}
}
protected void addRequiredBasePackage(String teamName, String baseName) {
List<String> bases = requiredBasePackagesPerTeam.get(teamName);
if (bases == null)
requiredBasePackagesPerTeam.put(teamName, bases = new ArrayList<>());
bases.add(baseName);
}
}
/** Super-role for access to internal members. */
protected class XMLAnalyzer playedBy XMLErrorReporter {
@SuppressWarnings("decapsulation")
protected String generateLocationPath(Node node, String attrName) -> String generateLocationPath(Node node, String attrName);
@SuppressWarnings("decapsulation")
protected IProject getFProject() -> get IProject fProject;
}
/**
* Detects aspectBindings declared in plugin.xml and records information in the current {@link BundleCheckingContext}.
* Directly reports erroneous use of '.' for nested team names.
*/
protected class ExtensionAnalyzer extends XMLAnalyzer playedBy ExtensionsErrorReporter
base when (BundleValidation.this.bundleContext.get() != null)
{
@SuppressWarnings("decapsulation")
State getState() -> get IPluginModelBase fModel
with { result <- fModel.getBundleDescription().getContainingState() }
VirtualMarker report(String message, int line, int severity, int fixId, String category)
-> VirtualMarker report(String message, int line, int severity, int fixId, String category);
@SuppressWarnings("decapsulation")
int getLine(Element element) -> int getLine(Element element);
@SuppressWarnings("decapsulation")
int getLine(Element element, String attrName) -> int getLine(Element element, String attrName);
VirtualMarker report(String message, int line, int severity, int fixId, Element element, String attrName, String category)
<- replace VirtualMarker report(String message, int line, int severity, int fixId, Element element, String attrName, String category);
void checkAspectBinding(Element element) <- after void validateExtension(Element element);
protected void checkAspectBinding(Element element)
{
Object pointID = element.getAttribute("point"); //$NON-NLS-1$
if (ASPECT_BINDING_FQEXTPOINT_ID.equals(pointID))
{
BundleCheckingContext context = BundleValidation.this.bundleContext.get();
// it's an aspect bundle
context.isAspectBundle = true;
IJavaProject jProject = JavaCore.create(context.getProject());
boolean hasSelfAdaptation = false;
NodeList baseNodes = element.getElementsByTagName(BASE_PLUGIN);
for (int b=0; b<baseNodes.getLength(); b++) {
if (SELF.equalsIgnoreCase(((Element)baseNodes.item(b)).getAttribute(ID))) {
hasSelfAdaptation = true;
break;
}
}
Map<String,Map<String,Set<String>>> superBasePackagesByTeam;
{
List<IMethodMapping> mappings = new ArrayList<>();
// collect binding requirements by nested teams of all bound teams:
NodeList teamNodes = element.getElementsByTagName(TEAM);
for (int t=0; t<teamNodes.getLength(); t++) {
// record aspect packages:
Object teamClass = ((Element)teamNodes.item(t)).getAttribute(CLASS);
if (teamClass instanceof String)
checkNestedTeams((String) teamClass, context, hasSelfAdaptation, mappings);
}
// collect packages with overridden base methods:
superBasePackagesByTeam = collectOverridden(mappings);
}
NodeList aspectBindings = element.getChildNodes();
int aspectCount = aspectBindings.getLength();
for (int i = 0; i < aspectCount; i++) {
Node aspectBinding = aspectBindings.item(i);
// does it have elements with relevant activation?
boolean isSelfAdaptation = false;
boolean hasActivation = true;
BundleDescription baseBundle = null;
List<String> teamNames = new ArrayList<String>();
NodeList children = aspectBinding.getChildNodes();
int childrenCount = children.getLength();
for (int j = 0; j < childrenCount; j++) {
Node child = children.item(j);
if (child instanceof Element) {
Element childElement = (Element)child;
String tagName = childElement.getTagName();
if (BASE_PLUGIN.equals(tagName)) {
String baseId = childElement.getAttribute(ID);
if (baseId != null) {
if (baseId.toUpperCase().equals(SELF))
isSelfAdaptation = true; // missing bundle activation is not fatal in this case
else
baseBundle = checkBasePlugIn(baseId, getLine(childElement));
}
} else if (TEAM.equals(tagName)) {
// analyze aspect packages:
Element teamNode = childElement;
Object teamClass = teamNode.getAttribute(CLASS);
if (!(teamClass instanceof String))
continue;
String teamName = (String) teamClass;
String actualPackage = checkActualPackage(context, teamNode, teamName);
if (actualPackage == null)
report(OTPDEUIMessages.Validation_MissingPackage_error, getLine(teamNode),
CompilerFlags.ERROR, PDEMarkerFactory.NO_RESOLUTION, PDEMarkerFactory.CAT_FATAL);
else
context.aspectPackages.add(actualPackage);
teamNames.add(teamName);
// team activation?
Object activation = teamNode.getAttribute(ACTIVATION);
if (ActivationKind.ALL_THREADS.toString().equals(activation)) {
hasActivation = true;
} else if (ActivationKind.THREAD.toString().equals(activation)) {
hasActivation = true;
}
NodeList superBases = teamNode.getElementsByTagName(SUPER_BASE);
for (int k=0; k<superBases.getLength(); k++) {
// report bad declarations & remove superBase requirements matching this declaration
Node grandChild = superBases.item(k);
if (grandChild instanceof Element) {
checkSuperBaseClass((Element) grandChild, superBasePackagesByTeam.get(teamName), jProject, baseBundle);
}
}
}
}
}
if (hasActivation && !isSelfAdaptation)
context.hasTeamActivation = true;
if (baseBundle != null) {
// remove packages provided by this baseBundle from the list of required packages
Set<String> providedPackages = new HashSet<String>();
// for SELF-adaptation we don't need a package export, that's why we include those
// requirements from this check (see checkNestedTeams(..hasSelfAdaptation)).
for (Capability cap : baseBundle.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE))
providedPackages.add((String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
for (String teamName: teamNames) {
List<String> basePackagesList = context.requiredBasePackagesPerTeam.get(teamName);
if (basePackagesList != null) {
Iterator<String> basePackages = basePackagesList.iterator();
while (basePackages.hasNext()) {
String basePackage = basePackages.next();
if (providedPackages.contains(basePackage))
basePackages.remove();
}
}
}
}
}
// complain about remaining requiredBasePackages (i.e., those for which no binding was provided)
reportUnmatchedRequirements(element, context.requiredBasePackagesPerTeam, e->e,
OTPDEUIMessages.Validation_MissingBindingForBasePackage_error);
// complain about remaining undeclared super bases:
reportUnmatchedRequirements(element, superBasePackagesByTeam,
e->e.values().stream().flatMap(Set::stream).collect(Collectors.toSet()),
OTPDEUIMessages.Validation_MissingSuperBasePackageDecl_error);
}
}
private <T> void reportUnmatchedRequirements(Element element,
Map<String, T> requirementsPerTeam,
Function<T,? extends Collection<String>> extractor,
String errorMessageTemplate)
{
for (Entry<String, T> entry : requirementsPerTeam.entrySet()) {
Collection<String> requireds = extractor.apply(entry.getValue());
if (requireds != null && !requireds.isEmpty()) {
for (String required : requireds) {
report(NLS.bind(errorMessageTemplate, entry.getKey(), required),
getLine(element),
CompilerFlags.ERROR,
PDEMarkerFactory.NO_RESOLUTION,
PDEMarkerFactory.CAT_FATAL);
}
}
}
}
private Map<String, Map<String,Set<String>>> collectOverridden(List<IMethodMapping> mappings) {
Map<String,Map<String,Set<String>>> superBasePackagesByTeam = new HashMap<>(); // inner map is package name -> class names
try {
// collect role types from mappings (IType, then ITypeBinding):
Set<IType> roleTypes = new HashSet<IType>();
for (IMethodMapping mapping : mappings) {
IType type = mapping.getDeclaringType();
if (!"java.lang.Object".equals(type.getSuperclassName())) //$NON-NLS-1$
roleTypes.add(type);
}
if (roleTypes.isEmpty())
return Collections.emptyMap();
ASTParser parser = ASTParser.newParser(AST.JLS12);
parser.setProject(JavaCore.create(getFProject()));
IBinding[] bindings = parser.createBindings(roleTypes.toArray(new IType[roleTypes.size()]), null);
// from ITypeBinding descend into IMethodMappingBinding, then IMethodBinding (base):
for (IBinding binding : bindings) {
if (binding instanceof ITypeBinding) {
String teamName = ((ITypeBinding) binding).getDeclaringClass().getQualifiedName();
Map<String,Set<String>> perTeamResult = superBasePackagesByTeam.get(teamName);
for (IMethodMappingBinding mappingBinding : ((ITypeBinding) binding).getResolvedMethodMappings()) {
if (mappingBinding == null) continue;
for (IMethodBinding basemethod : mappingBinding.getBaseMethods()) {
if (!mappingBinding.isCallin() && Flags.isPublic(basemethod.getModifiers()))
continue; // no weaving required for callout to public
// find overridden
for (IMethodBinding overriddenMethod : Bindings.findOverriddenMethods(basemethod, true, false)) {
if (Flags.isAbstract(overriddenMethod.getModifiers()))
continue; // no code to weave
// remember package of declaring class
ITypeBinding declaringClass = overriddenMethod.getDeclaringClass();
String packageName = declaringClass.getPackage().getName();
if (perTeamResult == null) {
superBasePackagesByTeam.put(teamName, perTeamResult = new HashMap<>());
}
Set<String> classSet = perTeamResult.get(packageName);
if (classSet == null) {
perTeamResult.put(packageName, classSet = new HashSet<>());
}
classSet.add(declaringClass.getQualifiedName());
}
}
}
}
}
} catch (JavaModelException e) {
// cannot analyse
}
return superBasePackagesByTeam;
}
private void checkSuperBaseClass(Element elem, Map<String,Set<String>> collectedPackages, IJavaProject jProject, BundleDescription baseBundle) {
try {
String packageName = null;
String superBaseClass = elem.getAttribute(SUPER_BASE_CLASS);
if (superBaseClass != null) {
IType clazz = jProject.findType(superBaseClass);
if (clazz != null) { // otherwise assume standard validation already complained
packageName = clazz.getPackageFragment().getElementName();
if (collectedPackages == null || collectedPackages.remove(packageName) == null) {
report(NLS.bind(OTPDEUIMessages.Validation_UnnecessarySuperBase_warning, superBaseClass),
getLine(elem),
CompilerFlags.WARNING,
PDEMarkerFactory.NO_RESOLUTION,
PDEMarkerFactory.CAT_OTHER);
}
}
}
if (packageName != null) {
String bundleName = elem.getAttribute(SUPER_BASE_PLUGIN);
BundleDescription basePlugIn = !bundleName.isEmpty() ? checkBasePlugIn(bundleName, getLine(elem))
: baseBundle; // fall back if no explicit plugin
if (basePlugIn != null) {
for (Capability cap : basePlugIn.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
if (packageName.equals(cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)))
return;
}
report(NLS.bind(OTPDEUIMessages.Validation_PackageNotInSuperBase_error, bundleName, packageName),
getLine(elem),
CompilerFlags.ERROR,
PDEMarkerFactory.NO_RESOLUTION,
PDEMarkerFactory.CAT_FATAL);
}
}
} catch (JavaModelException e) {
// cannot analyse
}
}
String checkActualPackage(BundleCheckingContext context, Element teamNode, String teamName) {
int lastDot = teamName.lastIndexOf('.');
if (lastDot == -1)
return null;
String packageName = teamName.substring(0, lastDot);
String actualPackage = getContainingPackage(context, packageName);
if (packageName != actualPackage) {
VirtualMarker marker = report(NLS.bind(OTPDEUIMessages.Validation_NotAPackage_error, packageName),
getLine(teamNode, CLASS),
CompilerFlags.ERROR,
CHANGE_DOT_TO_DOLLAR,
PDEMarkerFactory.CAT_FATAL);
if (marker != null) {
marker.setAttribute("package", actualPackage); //$NON-NLS-1$
marker.setAttribute("team", teamName); //$NON-NLS-1$
marker.setAttribute(PDEMarkerFactory.MPK_LOCATION_PATH, generateLocationPath(teamNode, CLASS));
}
}
return actualPackage;
}
String getContainingPackage(BundleCheckingContext context, String packageNameCandidate) {
IProject project = context.getProject();
if (project != null) {
IJavaProject jProject = JavaCore.create(project);
if (jProject != null) {
try {
IJavaElement jElement = jProject.findElement(new Path(packageNameCandidate));
if (jElement != null && jElement.getElementType() == IJavaElement.PACKAGE_FRAGMENT)
return packageNameCandidate;
jElement = jProject.findType(packageNameCandidate);
if (jElement != null) {
IJavaElement ancestor = jElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
if (ancestor != null)
return ancestor.getElementName();
}
} catch (JavaModelException e) {
// cannot analyse
}
}
}
return packageNameCandidate; // be shy about reporting errors in error contexts
}
BundleDescription checkBasePlugIn(String symbolicName, int lineNo) {
BundleDescription[] bundles = getState().getBundles(symbolicName);
if (bundles.length == 0) {
report(NLS.bind(OTPDEUIMessages.Validation_UnresolveBasePlugin_error, symbolicName),
lineNo,
CompilerFlags.ERROR,
PDEMarkerFactory.NO_RESOLUTION,
PDEMarkerFactory.CAT_OTHER);
return null;
}
return bundles[0];
}
void checkNestedTeams(String teamName, BundleCheckingContext context, boolean hasSelfAdaptation, List<IMethodMapping> mappings) {
teamName = teamName.replace('$', '.');
IJavaProject jPrj = JavaCore.create(getFProject());
if (jPrj.exists()) {
try {
IType teamType = jPrj.findType(teamName);
if (teamType != null) {
for (IType member : teamType.getTypes()) {
if (OTModelManager.isTeam(member)) {
String nestedTeamName = member.getFullyQualifiedName('$'); // name as used in aspectBinding.basePlugin
for (IType role : OTModelManager.getOTElement(member).getRoleTypes()) {
IType aBase = ((IRoleType) OTModelManager.getOTElement(role)).getBaseClass();
if (aBase != null
&& !(hasSelfAdaptation && aBase.getJavaProject().equals(jPrj)))
context.addRequiredBasePackage(nestedTeamName, aBase.getPackageFragment().getElementName());
}
checkNestedTeams(nestedTeamName, context, hasSelfAdaptation, mappings);
} else {
IRoleType role = (IRoleType) OTModelManager.getOTElement(member);
for (IMethodMapping mapping : role.getMethodMappings())
mappings.add(mapping);
}
}
}
} catch (JavaModelException e) {
// cannot analyse
}
}
}
@SuppressWarnings("basecall")
callin VirtualMarker report(String message, int line, int severity, int fixId, Element element, String attrName, String category) {
if (fixId == PDEMarkerFactory.M_DISCOURAGED_CLASS) {
if (matchElementPath(element, new String[] {ASPECT_BINDING, TEAM, SUPER_BASE}, 2))
return null; // don't report restriction inside aspectBinding/superBase
}
return base.report(message, line, severity, fixId, element, attrName, category);
}
private boolean matchElementPath(Element cur, String[] containerTags, int idx) {
if (idx < 0)
return true;
if (!containerTags[idx].equals(cur.getTagName()))
return false;
Node parentNode = cur.getParentNode();
if (parentNode instanceof Element)
return matchElementPath((Element) parentNode, containerTags, idx-1);
return false;
}
}
/**
* Validates whether activation policy is set if needed.
* This role is only active for bundles with one or more aspect bindings.
*/
protected class BundleErrorReporter playedBy BundleErrorReporter
base when (BundleValidation.this.bundleContext.get().isAspectBundle)
{
@SuppressWarnings("decapsulation")
void addMarkerAttribute(VirtualMarker marker, String attr, String val)
-> void addMarkerAttribute(VirtualMarker marker, String attr, String val);
@SuppressWarnings("decapsulation")
IHeader getHeader(String key) -> IHeader getHeader(String key);
VirtualMarker report(String message, int line, int severity, int resolution, String category)
-> VirtualMarker report(String message, int line, int severity, int resolution, String category);
void validateBundleActivatorPolicy() <- after void validateBundleActivatorPolicy();
void validateBundleActivatorPolicy()
{
IHeader header = getHeader(Constants.BUNDLE_ACTIVATIONPOLICY);
int lineNo = 1;
if (header != null) {
if (Constants.ACTIVATION_LAZY.equals(header.getValue()))
return; // OK!
lineNo = header.getLineNumber()+1;
}
boolean hasTeamActivation = BundleValidation.this.bundleContext.get().hasTeamActivation;
report(OTPDEUIMessages.Validation_MissingActivationPolicy_error,
lineNo,
hasTeamActivation ? CompilerFlags.ERROR : CompilerFlags.WARNING, // only severe if relevant team activation is requested.
ADD_ACTIVATION_POLICY,
PDEMarkerFactory.CAT_FATAL);
}
void validateExportPackages() <- after void validateExportPackages();
void validateExportPackages() {
Set<String> needingExport = bundleContext.get().aspectPackages;
if (needingExport.isEmpty()) return;
IHeader header = getHeader(Constants.EXPORT_PACKAGE);
if (header != null) {
ManifestElement[] elements = header.getElements();
for (int i = 0; i < elements.length; i++)
needingExport.remove(elements[i].getValue());
}
for (String unmatched : needingExport) {
VirtualMarker marker = report(NLS.bind(OTPDEUIMessages.Validation_MissingAspectPackageExport_error, unmatched),
1,
CompilerFlags.ERROR, // can reduce severity when we have the option to add the export at runtime
ADD_PACKAGE_EXPORT,
PDEMarkerFactory.CAT_FATAL);
addMarkerAttribute(marker, "package", unmatched); //$NON-NLS-1$
IHeader aspectBundleName = getHeader(Constants.BUNDLE_SYMBOLICNAME);
if (aspectBundleName != null && aspectBundleName.getValue() != null) {
String bundleSymbolicName = aspectBundleName.getValue();
int semi = bundleSymbolicName.indexOf(';');
if (semi != -1)
bundleSymbolicName = bundleSymbolicName.substring(0, semi); // strip of attributes/directives like ;singleton:=true
addMarkerAttribute(marker, "export", unmatched+";ot-aspect-host=\""+bundleSymbolicName+"\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
else {
addMarkerAttribute(marker, "export", unmatched); //$NON-NLS-1$
}
}
}
}
/** Unbound role: simple rewriting of the manifest to add or correct an activation policy header. */
protected class SetActivationPolicyResolution extends AbstractManifestMarkerResolution
{
public SetActivationPolicyResolution(int type) {
super(type);
}
protected void createChange(BundleModel model) {
IManifestHeader header = model.getBundle().getManifestHeader(Constants.BUNDLE_ACTIVATIONPOLICY);
if (header != null && header instanceof BundleActivationPolicyHeader)
((BundleActivationPolicyHeader) header).setLazyStart(true);
else
model.getBundle().setHeader(Constants.BUNDLE_ACTIVATIONPOLICY, Constants.ACTIVATION_LAZY);
}
public String getLabel() {
return OTPDEUIMessages.Resolution_AddBundleActivationPolicy_label;
}
}
/** Unbound role: simple rewriting of the manifest to add an Export-Package header. */
protected class ExportAspectPackageResolution extends AddExportPackageMarkerResolution {
String packageName;
String export; // extended version with ot-aspect-host attribute
public ExportAspectPackageResolution(IMarker marker) {
super(marker, AbstractPDEMarkerResolution.CREATE_TYPE, marker.getAttribute("export", null)); //$NON-NLS-1$
this.packageName = marker.getAttribute("package", null); //$NON-NLS-1$
this.export = marker.getAttribute("export", null); //$NON-NLS-1$
}
@Override
public String getLabel() {
return NLS.bind(OTPDEUIMessages.Resolution_AddAspectPackageExport_label, packageName);
}
@Override
public String getDescription() {
return NLS.bind(OTPDEUIMessages.Resolution_AddAspectPackageExport_description, packageName, export);
}
}
/** Unbound role: rewrite the team@class attribute for proper usage of '$' as inner class separator. */
protected class ChangeDotToDollarResolution extends AbstractXMLMarkerResolution {
String packageName;
String teamName;
String newName;
public ChangeDotToDollarResolution(IMarker marker) {
super(CHANGE_DOT_TO_DOLLAR, marker);
this.packageName = marker.getAttribute("package", null); //$NON-NLS-1$
this.teamName = marker.getAttribute("team", null); //$NON-NLS-1$
this.newName = packageName + '.' +teamName.substring(this.packageName.length()+1).replace('.', '$');
}
@Override
public String getLabel() {
return NLS.bind(OTPDEUIMessages.Resolution_ChangeDotToDollar_label, this.teamName);
}
@Override
public String getDescription() {
return NLS.bind(OTPDEUIMessages.Resolution_ChangeDotToDollar_description, this.teamName, this.newName);
}
@Override
protected void createChange(IPluginModelBase model) {
Object node = findNode(model);
if (!(node instanceof PluginAttribute))
return;
PluginAttribute attr = (PluginAttribute) node;
attr.getEnclosingElement().setXMLAttribute(attr.getName(), this.newName);
}
}
/**
* Advise the base class for handling missing/incorrect activation policy
* (code {@link BundleValidation#ADD_ACTIVATION_POLICY}).
*/
protected class ResolutionGenerator playedBy ResolutionGenerator {
IMarkerResolution[] getResolutions(IMarker marker) <- replace IMarkerResolution[] getResolutions(IMarker marker);
callin IMarkerResolution[] getResolutions(IMarker marker) {
IMarkerResolution[] result = base.getResolutions(marker);
if (result.length == 0) {
int problemID = marker.getAttribute(PDEMarkerFactory.PROBLEM_ID, PDEMarkerFactory.NO_RESOLUTION);
switch (problemID) {
case BundleValidation.ADD_ACTIVATION_POLICY :
return new IMarkerResolution[] {new SetActivationPolicyResolution(AbstractPDEMarkerResolution.CREATE_TYPE)};
case BundleValidation.ADD_PACKAGE_EXPORT :
return new IMarkerResolution[] {new ExportAspectPackageResolution(marker) };
case BundleValidation.CHANGE_DOT_TO_DOLLAR :
return new IMarkerResolution[] {new ChangeDotToDollarResolution(marker) };
}
}
return result;
}
}
}