/******************************************************************************* * Copyright (c) 2007, 2011 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.ui.text.correction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import org.eclipse.core.expressions.EvaluationContext; import org.eclipse.core.expressions.EvaluationResult; import org.eclipse.core.expressions.Expression; import org.eclipse.core.expressions.ExpressionConverter; import org.eclipse.core.expressions.ExpressionTagNames; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.ui.text.java.ClasspathFixProcessor; import org.eclipse.jdt.ui.text.java.ClasspathFixProcessor.ClasspathFixProposal; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.dialogs.StatusInfo; public final class ClasspathFixProcessorDescriptor { private static final String ATT_EXTENSION = "classpathFixProcessors"; //$NON-NLS-1$ private static final String ID= "id"; //$NON-NLS-1$ private static final String CLASS= "class"; //$NON-NLS-1$ private static final String OVERRIDES= "overrides"; //$NON-NLS-1$ private static ClasspathFixProcessorDescriptor[] fgContributedClasspathFixProcessors; private final IConfigurationElement fConfigurationElement; private ClasspathFixProcessor fProcessorInstance; private List<String> fOverriddenIds; private Boolean fStatus; public ClasspathFixProcessorDescriptor(IConfigurationElement element) { fConfigurationElement= element; fProcessorInstance= null; fStatus= null; // undefined if (fConfigurationElement.getChildren(ExpressionTagNames.ENABLEMENT).length == 0) { fStatus= Boolean.TRUE; } IConfigurationElement[] children= fConfigurationElement.getChildren(OVERRIDES); if (children.length > 0) { fOverriddenIds= new ArrayList<String>(children.length); for (int i= 0; i < children.length; i++) { fOverriddenIds.add(children[i].getAttribute(ID)); } } else { fOverriddenIds= Collections.emptyList(); } } public String getID() { return fConfigurationElement.getAttribute(ID); } public Collection<String> getOverridenIds() { return fOverriddenIds; } public IStatus checkSyntax() { IConfigurationElement[] children= fConfigurationElement.getChildren(ExpressionTagNames.ENABLEMENT); if (children.length > 1) { return new StatusInfo(IStatus.ERROR, "Only one < enablement > element allowed. Disabling " + getID()); //$NON-NLS-1$ } return new StatusInfo(IStatus.OK, "Syntactically correct classpath fix processor"); //$NON-NLS-1$ } public boolean matches(IJavaProject javaProject) { if (fStatus != null) { return fStatus.booleanValue(); } IConfigurationElement[] children= fConfigurationElement.getChildren(ExpressionTagNames.ENABLEMENT); if (children.length == 1) { try { ExpressionConverter parser= ExpressionConverter.getDefault(); Expression expression= parser.perform(children[0]); EvaluationContext evalContext= new EvaluationContext(null, javaProject); evalContext.addVariable("project", javaProject); //$NON-NLS-1$ evalContext.addVariable("sourceLevel", javaProject.getOption(JavaCore.COMPILER_SOURCE, true)); //$NON-NLS-1$ return expression.evaluate(evalContext) == EvaluationResult.TRUE; } catch (CoreException e) { JavaPlugin.log(e); } return false; } fStatus= Boolean.FALSE; return false; } public ClasspathFixProcessor getProcessor(IJavaProject project) { if (matches(project)) { if (fProcessorInstance == null) { try { Object extension= fConfigurationElement.createExecutableExtension(CLASS); if (extension instanceof ClasspathFixProcessor) { fProcessorInstance= (ClasspathFixProcessor) extension; } else { String message= "Invalid extension to " + ATT_EXTENSION //$NON-NLS-1$ + ". Must extends ClasspathFixProcessor: " + getID(); //$NON-NLS-1$ JavaPlugin.log(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, message)); fStatus= Boolean.FALSE; return null; } } catch (CoreException e) { JavaPlugin.log(e); fStatus= Boolean.FALSE; return null; } } return fProcessorInstance; } return null; } private static ClasspathFixProcessorDescriptor[] getCorrectionProcessors() { if (fgContributedClasspathFixProcessors == null) { IConfigurationElement[] elements= Platform.getExtensionRegistry().getConfigurationElementsFor(JavaUI.ID_PLUGIN, ATT_EXTENSION); ArrayList<ClasspathFixProcessorDescriptor> res= new ArrayList<ClasspathFixProcessorDescriptor>(elements.length); for (int i= 0; i < elements.length; i++) { ClasspathFixProcessorDescriptor desc= new ClasspathFixProcessorDescriptor(elements[i]); IStatus status= desc.checkSyntax(); if (status.isOK()) { res.add(desc); } else { JavaPlugin.log(status); } } fgContributedClasspathFixProcessors= res.toArray(new ClasspathFixProcessorDescriptor[res.size()]); Arrays.sort(fgContributedClasspathFixProcessors, new Comparator<ClasspathFixProcessorDescriptor>() { public int compare(ClasspathFixProcessorDescriptor d1, ClasspathFixProcessorDescriptor d2) { if (d1.getOverridenIds().contains(d2.getID())) { return -1; } if (d2.getOverridenIds().contains(d1.getID())) { return 1; } return 0; } }); } return fgContributedClasspathFixProcessors; } public static ClasspathFixProposal[] getProposals(final IJavaProject project, final String missingType, final MultiStatus status) { final ArrayList<ClasspathFixProposal> proposals= new ArrayList<ClasspathFixProposal>(); final HashSet<String> overriddenIds= new HashSet<String>(); ClasspathFixProcessorDescriptor[] correctionProcessors= getCorrectionProcessors(); for (int i= 0; i < correctionProcessors.length; i++) { final ClasspathFixProcessorDescriptor curr= correctionProcessors[i]; if (!overriddenIds.contains(curr.getID())) { SafeRunner.run(new ISafeRunnable() { public void run() throws Exception { ClasspathFixProcessor processor= curr.getProcessor(project); if (processor != null) { ClasspathFixProposal[] fixProposals= processor.getFixImportProposals(project, missingType); if (fixProposals != null) { for (int k= 0; k < fixProposals.length; k++) { proposals.add(fixProposals[k]); } overriddenIds.addAll(curr.getOverridenIds()); } } } public void handleException(Throwable exception) { if (status != null) { status.merge(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IStatus.ERROR, CorrectionMessages.ClasspathFixProcessorDescriptor_error_processing_processors, exception)); } } }); } } return proposals.toArray(new ClasspathFixProposal[proposals.size()]); } }