Copyright (c) 2005, 2017 IBM Corporation and others. 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 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2005, 2017 IBM Corporation and others. * * 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 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.internal.launching; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.expressions.PropertyTester; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.IOrdinaryClassFile; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.ToolFactory; import org.eclipse.jdt.core.compiler.IScanner; import org.eclipse.jdt.core.compiler.ITerminalSymbols; import org.eclipse.jdt.core.compiler.InvalidInputException;
Property tester for context launching menu.
Since:3.2
/** * Property tester for context launching menu. * * @since 3.2 */
public class JavaLaunchableTester extends PropertyTester {
name for the "has main" property
/** * name for the "has main" property */
private static final String PROPERTY_HAS_MAIN = "hasMain"; //$NON-NLS-1$
name for the "has method" property
/** * name for the "has method" property */
private static final String PROPERTY_HAS_METHOD = "hasMethod"; //$NON-NLS-1$
name for the "has method with annotation" property
/** * name for the "has method with annotation" property */
private static final String PROPERTY_HAS_METHOD_WITH_ANNOTATION = "hasMethodWithAnnotation"; //$NON-NLS-1$
name for the "has type with annotation" property
/** * name for the "has type with annotation" property */
private static final String PROPERTY_HAS_TYPE_WITH_ANNOTATION = "hasTypeWithAnnotation"; //$NON-NLS-1$
name for the "extends class" property
/** * name for the "extends class" property */
private static final String PROPERTY_EXTENDS_CLASS = "extendsClass"; //$NON-NLS-1$
"is container" property
/** * "is container" property */
private static final String PROPERTY_IS_CONTAINER = "isContainer"; //$NON-NLS-1$
"is package fragment" property
Since:3.3
/** * "is package fragment" property * @since 3.3 */
private static final String PROPERTY_IS_PACKAGE_FRAGMENT = "isPackageFragment"; //$NON-NLS-1$
"is package fragment root" property
Since:3.3
/** * "is package fragment root" property * @since 3.3 */
private static final String PROPERTY_IS_PACKAGE_FRAGMENT_ROOT = "isPackageFragmentRoot"; //$NON-NLS-1$
name for the PROPERTY_PROJECT_NATURE property
/** * name for the PROPERTY_PROJECT_NATURE property */
private static final String PROPERTY_PROJECT_NATURE = "hasProjectNature"; //$NON-NLS-1$
name for the "extends interface" property
/** * name for the "extends interface" property */
private static final String PROPERTY_EXTENDS_INTERFACE = "extendsInterface"; //$NON-NLS-1$
name for the PROPERTY_HAS_SWT_ON_PATH property
/** * name for the PROPERTY_HAS_SWT_ON_PATH property */
private static final String PROPERTY_BUILDPATH_REFERENCE = "buildpathReference"; //$NON-NLS-1$
Map of modifier text to corresponding flag (Integer)
/** * Map of modifier text to corresponding flag (Integer) */
private static Map<String, Integer> fgModifiers = new HashMap<>(); private static final int FLAGS_MASK= Flags.AccPublic | Flags.AccProtected | Flags.AccPrivate | Flags.AccStatic | Flags.AccFinal | Flags.AccSynchronized | Flags.AccAbstract | Flags.AccNative; static { fgModifiers.put("public", new Integer(Flags.AccPublic)); //$NON-NLS-1$ fgModifiers.put("protected", new Integer(Flags.AccProtected)); //$NON-NLS-1$ fgModifiers.put("private", new Integer(Flags.AccPrivate)); //$NON-NLS-1$ fgModifiers.put("static", new Integer(Flags.AccStatic)); //$NON-NLS-1$ fgModifiers.put("final", new Integer(Flags.AccFinal)); //$NON-NLS-1$ fgModifiers.put("synchronized", new Integer(Flags.AccSynchronized)); //$NON-NLS-1$ fgModifiers.put("abstract", new Integer(Flags.AccAbstract)); //$NON-NLS-1$ fgModifiers.put("native", new Integer(Flags.AccNative)); //$NON-NLS-1$ }
gets the type of the IJavaElement
Params:
  • element – the element to inspect
Returns:the type
/** * gets the type of the IJavaElement * @param element the element to inspect * @return the type */
private IType getType(IJavaElement element) { IType type = null; if (element instanceof ICompilationUnit) { type= ((ICompilationUnit) element).findPrimaryType(); } else if (element instanceof IOrdinaryClassFile) { type = ((IOrdinaryClassFile) element).getType(); } else if (element instanceof IType) { type = (IType) element; } else if (element instanceof IMember) { type = ((IMember)element).getDeclaringType(); } return type; }
Determines is the java element contains a main method.
Params:
  • element – the element to check for the method
Returns:true if the method is found in the element, false otherwise
/** * Determines is the java element contains a main method. * * @param element the element to check for the method * @return true if the method is found in the element, false otherwise */
private boolean hasMain(IJavaElement element) { try { IType type = getType(element); if(type != null && type.exists()) { if(hasMainMethod(type)) { return true; } //failed to find in public type, check static inner types IJavaElement[] children = type.getChildren(); for(int i = 0; i < children.length; i++) { if(hasMainInChildren(getType(children[i]))) { return true; } } } } catch (JavaModelException e) {} catch (CoreException ce){} return false; }
Returns if the specified IType has a main method
Params:
  • type – the type to inspect for a main type
Throws:
Returns:true if the specified type has a main method, false otherwise
Since:3.3
/** * Returns if the specified <code>IType</code> has a main method * @param type the type to inspect for a main type * @return true if the specified type has a main method, false otherwise * @throws JavaModelException if there is an error in the backing Java model * @since 3.3 */
private boolean hasMainMethod(IType type) throws JavaModelException { IMethod[] methods = type.getMethods(); for (int i= 0; i < methods.length; i++) { if(methods[i].isMainMethod()) { return true; } } return false; }
This method asks the specified IType if it has a main method, if not it recurses through all of its children When recursing we only care about child ITypes that are static.
Params:
  • type – the IType to inspect for a main method
Throws:
Returns:true if a main method was found in specified IType, false otherwise
Since:3.3
/** * This method asks the specified <code>IType</code> if it has a main method, if not it recurses through all of its children * When recursing we only care about child <code>IType</code>s that are static. * @param type the <code>IType</code> to inspect for a main method * @return true if a main method was found in specified <code>IType</code>, false otherwise * @throws CoreException if there is an error * @since 3.3 */
private boolean hasMainInChildren(IType type) throws CoreException { if(type.isClass() & Flags.isStatic(type.getFlags())) { if(hasMainMethod(type)) { return true; } IJavaElement[] children = type.getChildren(); for(int i = 0; i < children.length; i++) { if(children[i].getElementType() == IJavaElement.TYPE) { return hasMainInChildren((IType) children[i]); } } } return false; }
Determines is the java element contains a specific method.

The syntax for the property tester is of the form: methodname, signature, modifiers.

  1. methodname - case sensitive method name, required. For example, toString.
  2. signature - JLS style method signature, required. For example, (QString;)V.
  3. modifiers - optional space separated list of modifiers, for example, public static.
Params:
  • element – the element to check for the method
  • args – first arg is method name, secondary args are parameter types signatures
Returns:true if the method is found in the element, false otherwise
/** * Determines is the java element contains a specific method. * <p> * The syntax for the property tester is of the form: methodname, * signature, modifiers. * </p> * <ol> * <li>methodname - case sensitive method name, required. For example, * <code>toString</code>.</li> * <li>signature - JLS style method signature, required. For example, * <code>(QString;)V</code>.</li> * <li>modifiers - optional space separated list of modifiers, for * example, <code>public static</code>.</li> * </ol> * @param element the element to check for the method * @param args first arg is method name, secondary args are parameter types signatures * @return true if the method is found in the element, false otherwise */
private boolean hasMethod(IJavaElement element, Object[] args) { try { if (args.length > 1) { IType type = getType(element); if (type != null && type.exists()) { String name = (String) args[0]; String signature = (String) args[1]; String[] parms = Signature.getParameterTypes(signature); String returnType = Signature.getReturnType(signature); IMethod candidate = type.getMethod(name, parms); if (candidate.exists()) { // check return type if (candidate.getReturnType().equals(returnType)) { // check modifiers if (args.length > 2) { String modifierText = (String) args[2]; String[] modifiers = modifierText.split(" "); //$NON-NLS-1$ int flags = 0; for (int j = 0; j < modifiers.length; j++) { String modifier = modifiers[j]; Integer flag = fgModifiers.get(modifier); if (flag != null) { flags = flags | flag.intValue(); } } if (candidate.getFlags() == flags) { return true; } } } } } } } catch (JavaModelException e) {} return false; }
Determines is the java element contains a type with a specific annotation.

The syntax for the property tester is of the form: qualified or unqualified annotation name

  • qualified or unqualified annotation name, required. For example, org.junit.JUnit.
  • Params:
    • element – the element to check for the method
    • annotationType – the qualified or unqualified name of the annotation kind to look for
    Returns:true if the type is found in the element, false otherwise
    /** * Determines is the java element contains a type with a specific annotation. * <p> * The syntax for the property tester is of the form: qualified or unqualified annotation name * <li>qualified or unqualified annotation name, required. For example, * <code>org.junit.JUnit</code>.</li> * </ol> * @param element the element to check for the method * @param annotationType the qualified or unqualified name of the annotation kind to look for * @return true if the type is found in the element, false otherwise */
    private boolean hasTypeWithAnnotation(IJavaElement element, String annotationType) { try { IType type= getType(element); if (type == null || !type.exists()) { return false; } IBuffer buffer= null; IOpenable openable= type.getOpenable(); if (openable instanceof ICompilationUnit) { buffer= ((ICompilationUnit) openable).getBuffer(); } else if (openable instanceof IClassFile) { buffer= ((IClassFile) openable).getBuffer(); } if (buffer == null) { return false; } ISourceRange sourceRange= type.getSourceRange(); ISourceRange nameRange= type.getNameRange(); if (sourceRange != null && nameRange != null) { IScanner scanner= ToolFactory.createScanner(false, false, true, false); scanner.setSource(buffer.getCharacters()); scanner.resetTo(sourceRange.getOffset(), nameRange.getOffset()); if (findAnnotation(scanner, annotationType)) { return true; } } } catch (JavaModelException e) {} catch (InvalidInputException e) {} return false; }
    Determines is the java element contains a method with a specific annotation.

    The syntax for the property tester is of the form: qualified or unqualified annotation name, modifiers

  • qualified or unqualified annotation name, required. For example, org.junit.JUnit.
  • modifiers - optional space separated list of modifiers, for example, public static.
  • Params:
    • element – the element to check for the method
    • args – the arguments
    Returns:true if the method is found in the element, false otherwise
    /** * Determines is the java element contains a method with a specific annotation. * <p> * The syntax for the property tester is of the form: qualified or unqualified annotation name, modifiers * <li>qualified or unqualified annotation name, required. For example, * <code>org.junit.JUnit</code>.</li> * <li>modifiers - optional space separated list of modifiers, for * example, <code>public static</code>.</li> * </ol> * @param element the element to check for the method * @param args the arguments * @return true if the method is found in the element, false otherwise */
    private boolean hasMethodWithAnnotation(IJavaElement element, Object[] args) { try { String annotationType= (String) args[0]; int flags = 0; if (args.length > 1) { String[] modifiers = ((String) args[1]).split(" "); //$NON-NLS-1$ for (int j = 0; j < modifiers.length; j++) { String modifier = modifiers[j]; Integer flag = fgModifiers.get(modifier); if (flag != null) { flags = flags | flag.intValue(); } } } else { flags= -1; } IType type= getType(element); if (type == null || !type.exists()) { return false; } IMethod[] methods= type.getMethods(); if (methods.length == 0) { return false; } IBuffer buffer= null; IOpenable openable= type.getOpenable(); if (openable instanceof ICompilationUnit) { buffer= ((ICompilationUnit) openable).getBuffer(); } else if (openable instanceof IClassFile) { buffer= ((IClassFile) openable).getBuffer(); } if (buffer == null) { return false; } IScanner scanner=null; // delay initialization for (int i= 0; i < methods.length; i++) { IMethod curr= methods[i]; if (curr.isConstructor() || (flags != -1 && flags != (curr.getFlags() & FLAGS_MASK))) { continue; } ISourceRange sourceRange= curr.getSourceRange(); ISourceRange nameRange= curr.getNameRange(); if (sourceRange != null && nameRange != null) { if (scanner == null) { scanner= ToolFactory.createScanner(false, false, true, false); scanner.setSource(buffer.getCharacters()); } scanner.resetTo(sourceRange.getOffset(), nameRange.getOffset()); if (findAnnotation(scanner, annotationType)) { return true; } } } } catch (JavaModelException e) { } catch (InvalidInputException e) { } return false; } private boolean findAnnotation(IScanner scanner, String annotationName) throws InvalidInputException { String simpleName= Signature.getSimpleName(annotationName); StringBuilder buf= new StringBuilder(); int tok= scanner.getNextToken(); while (tok != ITerminalSymbols.TokenNameEOF) { if (tok == ITerminalSymbols.TokenNameAT) { buf.setLength(0); tok= readName(scanner, buf); String name= buf.toString(); if (name.equals(annotationName) || name.equals(simpleName) || name.endsWith('.' + simpleName)) { return true; } } else { tok= scanner.getNextToken(); } } return false; } private int readName(IScanner scanner, StringBuilder buf) throws InvalidInputException { int tok= scanner.getNextToken(); while (tok == ITerminalSymbols.TokenNameIdentifier) { buf.append(scanner.getCurrentTokenSource()); tok= scanner.getNextToken(); if (tok != ITerminalSymbols.TokenNameDOT) { return tok; } buf.append('.'); tok= scanner.getNextToken(); } return tok; }
    determines if the project selected has the specified nature
    Params:
    • element – the Java element to get the project from
    • ntype – the specified nature type
    Returns:true if the specified nature matches the project, false otherwise
    /** * determines if the project selected has the specified nature * @param element the Java element to get the project from * @param ntype the specified nature type * @return true if the specified nature matches the project, false otherwise */
    private boolean hasProjectNature(IJavaElement element, String ntype) { try { if(element != null) { IJavaProject jproj = element.getJavaProject(); if(jproj != null) { IProject proj = jproj.getProject(); return proj.isAccessible() && proj.hasNature(ntype); } } return false; } catch (CoreException e) {return false;} }
    Determines if the element has qname as a parent class
    Params:
    • element – the element to check for the parent class definition
    • qname – the fully qualified name of the (potential) parent class
    Returns:true if qname is a parent class, false otherwise
    /** * Determines if the element has qname as a parent class * @param element the element to check for the parent class definition * @param qname the fully qualified name of the (potential) parent class * @return true if qname is a parent class, false otherwise */
    private boolean hasSuperclass(IJavaElement element, String qname) { try { IType type = getType(element); if(type != null) { IType[] stypes = type.newSupertypeHierarchy(new NullProgressMonitor()).getAllSuperclasses(type); for(int i = 0; i < stypes.length; i++) { if(stypes[i].getFullyQualifiedName().equals(qname) || stypes[i].getElementName().equals(qname)) { return true; } } } } catch(JavaModelException e) {} return false; }
    Determines if an item or list of items are found on the build path. Once any one single items matches though, the method returns true, this method is intended to be used in OR like situations, where we do not care if all of the items are on the build path, only that one of them is.
    Params:
    • element – the element whose build path should be checked
    • args – the value(s) to search for on the build path
    Returns:true if any one of the args is found on the build path
    /** * Determines if an item or list of items are found on the build path. * Once any one single items matches though, the method returns true, this method is intended * to be used in OR like situations, where we do not care if all of the items are on the build path, only that one * of them is. * * @param element the element whose build path should be checked * @param args the value(s) to search for on the build path * @return true if any one of the args is found on the build path */
    private boolean hasItemOnBuildPath(IJavaElement element, Object[] args) { if(element != null && args != null) { IJavaProject project = element.getJavaProject(); Set<IJavaProject> searched = new HashSet<>(); searched.add(project); return hasItemsOnBuildPath(project, searched, args); } return false; } private boolean hasItemsOnBuildPath(IJavaProject project, Set<IJavaProject> searched, Object[] args) { try { List<IJavaProject> projects = new ArrayList<>(); if(project != null && project.exists()) { IClasspathEntry[] entries = project.getResolvedClasspath(true); for(int i = 0; i < entries.length; i++) { IClasspathEntry entry = entries[i]; IPath path = entry.getPath(); String spath = path.toPortableString(); for(int j = 0; j < args.length; j++) { if(spath.lastIndexOf((String)args[j]) != -1) { return true; } } if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { String name = entry.getPath().lastSegment(); IProject dep = ResourcesPlugin.getWorkspace().getRoot().getProject(name); IJavaProject javaProject = JavaCore.create(dep); if (!searched.contains(javaProject)) { projects.add(javaProject); } } } } // search referenced projects Iterator<IJavaProject> iterator = projects.iterator(); while (iterator.hasNext()) { IJavaProject jp = iterator.next(); searched.add(jp); if (hasItemsOnBuildPath(jp, searched, args)) { return true; } } } catch (JavaModelException e) {} return false; }
    determines if the element implements a given interface
    Params:
    • element – the element to check for the interface
    • qname – the fully qualified name of the interface to check for
    Returns:true if the element does implement the interface, false otherwise
    /** * determines if the element implements a given interface * @param element the element to check for the interface * @param qname the fully qualified name of the interface to check for * @return true if the element does implement the interface, false otherwise */
    private boolean implementsInterface(IJavaElement element, String qname) { try { IType type = getType(element); if(type != null) { IType[] itypes = type.newSupertypeHierarchy(new NullProgressMonitor()).getAllInterfaces(); for(int i = 0; i < itypes.length; i++) { if(itypes[i].getFullyQualifiedName().equals(qname)) { return true; } } } } catch(JavaModelException e) {} return false; }
    Method runs the tests defined from extension points for Run As... and Debug As... menu items. Currently this test optimistically considers everything not a source file. In this context we consider an optimistic approach to mean that the test will always return true. There are many reasons for the optimistic choice some of them are outlined below.
    • Performance (in terms of time needed to display menu) cannot be preserved. To know what to allow in any one of the menus we would have to search all of the children of the container to determine what it contains and what can be launched by what.
    • If inspection of children of containers were done, a user might want to choose a different launch type, even though our tests filter it out.
    See Also:
    • test.test(Object, String, Object[], Object)
    Since:3.2
    Returns:true if the specified tests pass, or the context is a container, false otherwise
    /** * Method runs the tests defined from extension points for Run As... and Debug As... menu items. * Currently this test optimistically considers everything not a source file. In this context we * consider an optimistic approach to mean that the test will always return true. * * There are many reasons for the optimistic choice some of them are outlined below. * <ul> * <li>Performance (in terms of time needed to display menu) cannot be preserved. To know what to allow * in any one of the menus we would have to search all of the children of the container to determine what it contains * and what can be launched by what.</li> * <li>If inspection of children of containers were done, a user might want to choose a different launch type, even though our tests * filter it out.</li> * </ul> * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object, java.lang.String, java.lang.Object[], java.lang.Object) * @since 3.2 * @return true if the specified tests pass, or the context is a container, false otherwise */
    @Override public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { if (PROPERTY_IS_CONTAINER.equals(property)) { if (receiver instanceof IAdaptable) { IResource resource = ((IAdaptable) receiver).getAdapter(IResource.class); if (resource != null) { return resource instanceof IContainer; } } return false; } IJavaElement element = null; if (receiver instanceof IAdaptable) { element = ((IAdaptable) receiver).getAdapter(IJavaElement.class); if(element != null) { if(!element.exists()) { return false; } } } if(PROPERTY_HAS_MAIN.equals(property)) { return hasMain(element); } if (PROPERTY_HAS_METHOD.equals(property)) { return hasMethod(element, args); } if (PROPERTY_HAS_METHOD_WITH_ANNOTATION.equals(property)) { return hasMethodWithAnnotation(element, args); } if (PROPERTY_HAS_TYPE_WITH_ANNOTATION.equals(property)) { return hasTypeWithAnnotation(element, (String)args[0]); } if(PROPERTY_BUILDPATH_REFERENCE.equals(property)) { return hasItemOnBuildPath(element, args); } if(PROPERTY_EXTENDS_CLASS.equals(property)) { return hasSuperclass(element, (String)args[0]); } if(PROPERTY_PROJECT_NATURE.equals(property)) { return hasProjectNature(element, (String)args[0]); } if(PROPERTY_EXTENDS_INTERFACE.equals(property)) { return implementsInterface(element, (String)args[0]); } if (PROPERTY_IS_PACKAGE_FRAGMENT.equals(property)) { return element instanceof IPackageFragment; } if (PROPERTY_IS_PACKAGE_FRAGMENT_ROOT.equals(property)) { return element instanceof IPackageFragmentRoot; } return false; } }