/* * Copyright (c) 2007-2009, Osmorc Development Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.osmorc.inspection; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInspection.*; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.osgi.framework.BundleActivator; import org.osmorc.BundleManager; import org.osmorc.facet.OsmorcFacet; import org.osmorc.facet.OsmorcFacetConfiguration; import org.osmorc.manifest.BundleManifest; import org.osmorc.manifest.lang.psi.Header; import org.osmorc.manifest.lang.psi.Section; import org.osmorc.manifest.lang.ManifestTokenType; /** * Inspection that reports classes implementing BundleActivator which are not registered in the manifest / facet * config. * * @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a> * @author Robert F. Beeger (robert@beeger.net) * @version $Id$ */ public class UnregisteredActivatorInspection extends LocalInspectionTool { @Nls @NotNull public String getGroupDisplayName() { return "OSGi"; } public boolean isEnabledByDefault() { return true; } @NotNull public HighlightDisplayLevel getDefaultLevel() { return HighlightDisplayLevel.ERROR; } @Nls @NotNull public String getDisplayName() { return "Bundle Activator not registered"; } @NonNls @NotNull public String getShortName() { return "osmorcUnregisteredActivator"; } @NotNull public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { public void visitReferenceExpression(PsiReferenceExpression expression) { } @Override public void visitClass(PsiClass psiClass) { if (OsmorcFacet.hasOsmorcFacet(psiClass)) { PsiType[] types = psiClass.getSuperTypes(); for (PsiType type : types) { if (type.equalsToText(BundleActivator.class.getName())) { // okay extends bundle activator OsmorcFacetConfiguration configuration = OsmorcFacet.getInstance(psiClass).getConfiguration(); String activatorName = psiClass.getQualifiedName(); // if manifest is manually written, look it up in the manifest file if (configuration.isManifestManuallyEdited()) { BundleManager bundleManager = ServiceManager.getService(psiClass.getProject(), BundleManager.class); Module module = ModuleUtil.findModuleForPsiElement(psiClass); if (!isActivatorRegistered(bundleManager, module, activatorName)) { assert activatorName != null; holder.registerProblem(psiClass.getNameIdentifier(), "Bundle activator is not registered in manifest.", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RegisterActivatorInManifestQuickfix( activatorName, bundleManager.getBundleManifest(module).getManifestFile())); } } else { // automagically, so look it up in the configuration String configuredActivator = configuration.getBundleActivator(); if (!configuredActivator.equals(activatorName)) { holder.registerProblem(psiClass.getNameIdentifier(), "Bundle activator is not set up in facet configuration.", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RegisterActivatorInConfigurationQuickfix(activatorName, configuration)); } } } } } } }; } private boolean isActivatorRegistered(BundleManager manager, Object bundle, String activatorName) { BundleManifest manifest = manager.getBundleManifest(bundle); if (manifest != null) { String manifestActivator = manifest.getBundleActivator(); return manifestActivator != null && manifestActivator.equals(activatorName); } return true; } private class RegisterActivatorInManifestQuickfix implements LocalQuickFix { private static final String NAME = "Register Activator In Manifest"; private static final String FAMILY = "Osmorc"; private final String activatorClassName; private final PsiFile manifestFile; private RegisterActivatorInManifestQuickfix(@NotNull final String activatorClassName, @NotNull final PsiFile manifestFile) { this.activatorClassName = activatorClassName; this.manifestFile = manifestFile; } @NotNull public String getName() { return NAME; } @NotNull public String getFamilyName() { return FAMILY; } public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { ReadonlyStatusHandler.OperationStatus status = ReadonlyStatusHandler.getInstance(manifestFile.getProject()).ensureFilesWritable(manifestFile.getVirtualFile()); if (!status.hasReadonlyFiles()) { Section mainSection = (Section) manifestFile.getFirstChild(); Header activatorHeader = null; Header currentHeader = PsiTreeUtil.getChildOfType(mainSection, Header.class); while (activatorHeader == null && currentHeader != null) { if ("Bundle-Activator".equalsIgnoreCase(currentHeader.getName())) { activatorHeader = currentHeader; } currentHeader = PsiTreeUtil.getNextSiblingOfType(currentHeader, Header.class); } if (activatorHeader != null) { replaceExistingActivatorHeader(activatorHeader); } else { addActivatorHeader(); } } } private void addActivatorHeader() { PsiFile fromText = PsiFileFactory.getInstance(manifestFile.getProject()).createFileFromText("DUMMY.MF", String.format("Bundle-Activator: %s\n", activatorClassName)); Header newheader = PsiTreeUtil.getChildOfType(fromText.getFirstChild(), Header.class); assert newheader != null; Section section = (Section) manifestFile.getFirstChild(); addMissiingNewline(section); section.add(newheader); } private void addMissiingNewline(Section section) { String sectionText = section.getText(); if (sectionText.charAt(sectionText.length() - 1) != '\n') { PsiElement lastChild = section.getLastChild(); if (lastChild instanceof Header) { Header header = (Header) lastChild; header.getNode().addLeaf(ManifestTokenType.NEWLINE, "\n", null); } } } private void replaceExistingActivatorHeader(Header activatorHeader) { String headerFormatString = "Bundle-Activator: %s\n"; PsiFile fromText = PsiFileFactory.getInstance(manifestFile.getProject()).createFileFromText("DUMMY.MF", String.format(headerFormatString, activatorClassName)); Header newheader = PsiTreeUtil.getChildOfType(fromText.getFirstChild(), Header.class); assert newheader != null; activatorHeader.replace(newheader); } } private class RegisterActivatorInConfigurationQuickfix implements LocalQuickFix { private static final String NAME = "Register Activator In Configuration"; private static final String FAMILY = "Osmorc"; private final String activatorClassName; private final OsmorcFacetConfiguration configuration; private RegisterActivatorInConfigurationQuickfix(@NotNull final String activatorClassName, @NotNull final OsmorcFacetConfiguration configuration) { this.activatorClassName = activatorClassName; this.configuration = configuration; } @NotNull public String getName() { return NAME; } @NotNull public String getFamilyName() { return FAMILY; } public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { configuration.setBundleActivator(activatorClassName); } } }