blob: 38ba801f7417a553fe7e250e6b2ce3c5cbafd902 [file] [log] [blame]
/*
* Copyright (c) 2010-2020 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.core.model.ecj;
import static java.util.Collections.emptyList;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.scout.sdk.core.model.api.Flags;
import org.eclipse.scout.sdk.core.model.api.IMethod;
import org.eclipse.scout.sdk.core.model.api.ISourceRange;
import org.eclipse.scout.sdk.core.model.api.internal.MethodImplementor;
import org.eclipse.scout.sdk.core.model.spi.CompilationUnitSpi;
import org.eclipse.scout.sdk.core.model.spi.JavaElementSpi;
import org.eclipse.scout.sdk.core.model.spi.MethodParameterSpi;
import org.eclipse.scout.sdk.core.model.spi.MethodSpi;
import org.eclipse.scout.sdk.core.model.spi.TypeParameterSpi;
import org.eclipse.scout.sdk.core.model.spi.TypeSpi;
import org.eclipse.scout.sdk.core.util.Ensure;
import org.eclipse.scout.sdk.core.util.FinalValue;
/**
*
*/
public class BindingMethodWithEcj extends AbstractMemberWithEcj<IMethod> implements MethodSpi {
private final BindingTypeWithEcj m_declaringType;
private final MethodBinding m_binding;
private final FinalValue<TypeSpi> m_returnType;
private final FinalValue<List<BindingAnnotationWithEcj>> m_annotations;
private final FinalValue<String> m_name;
private final FinalValue<List<TypeSpi>> m_exceptions;
private final FinalValue<List<MethodParameterSpi>> m_arguments;
private final FinalValue<List<TypeParameterSpi>> m_typeParameters;
private final FinalValue<ISourceRange> m_source;
private final FinalValue<ISourceRange> m_bodySource;
private final FinalValue<ISourceRange> m_javaDocSource;
private int m_flags;
protected BindingMethodWithEcj(JavaEnvironmentWithEcj env, BindingTypeWithEcj declaringType, MethodBinding binding) {
super(env);
m_declaringType = Ensure.notNull(declaringType);
m_binding = Ensure.notNull(binding);
m_flags = -1;
m_returnType = new FinalValue<>();
m_annotations = new FinalValue<>();
m_name = new FinalValue<>();
m_exceptions = new FinalValue<>();
m_arguments = new FinalValue<>();
m_typeParameters = new FinalValue<>();
m_source = new FinalValue<>();
m_bodySource = new FinalValue<>();
m_javaDocSource = new FinalValue<>();
}
@Override
public JavaElementSpi internalFindNewElement() {
return SpiWithEcjUtils.findNewMethodIn(this);
}
@Override
protected IMethod internalCreateApi() {
return new MethodImplementor(this);
}
public MethodBinding getInternalBinding() {
return m_binding;
}
@Override
public List<BindingAnnotationWithEcj> getAnnotations() {
return m_annotations.computeIfAbsentAndGet(() -> SpiWithEcjUtils.createBindingAnnotations(this, SpiWithEcjUtils.nvl(m_binding.original(), m_binding)));
}
@Override
public int getFlags() {
if (m_flags < 0) {
m_flags = SpiWithEcjUtils.getMethodFlags(m_binding.modifiers, false, SpiWithEcjUtils.hasDeprecatedAnnotation(getAnnotations()));
}
return m_flags;
}
@Override
public String getElementName() {
return m_name.computeIfAbsentAndGet(() -> {
if (m_binding.isConstructor()) {
return m_declaringType.getElementName();
}
return new String(m_binding.selector);
});
}
@Override
public List<TypeSpi> getExceptionTypes() {
return m_exceptions.computeIfAbsentAndGet(() -> SpiWithEcjUtils.bindingsToTypes(javaEnvWithEcj(), m_binding.thrownExceptions));
}
@Override
public TypeSpi getReturnType() {
return m_returnType.computeIfAbsentAndGet(() -> {
if (isConstructor()) {
return null;
}
return SpiWithEcjUtils.bindingToType(javaEnvWithEcj(), m_binding.returnType);
});
}
@Override
public List<MethodParameterSpi> getParameters() {
return m_arguments.computeIfAbsentAndGet(() -> {
TypeBinding[] arguments = m_binding.parameters;
if (arguments == null || arguments.length < 1) {
return emptyList();
}
List<MethodParameterSpi> result = new ArrayList<>(arguments.length);
MethodBinding originalBinding = m_binding.original();
AbstractMethodDeclaration sourceMethod = m_binding.sourceMethod();
AbstractMethodDeclaration originalSourceMethod = originalBinding.sourceMethod();
for (int i = 0; i < arguments.length; i++) {
char[] name = getParamName(m_binding, sourceMethod, i);
if (name == null) {
name = getParamName(originalBinding, originalSourceMethod, i);
if (name == null) {
name = ("arg" + i).toCharArray();
}
}
int flags = Flags.AccDefault;
if (sourceMethod != null && sourceMethod.arguments.length > i) {
flags = sourceMethod.arguments[i].modifiers;
}
else if (originalSourceMethod != null && originalSourceMethod.arguments.length > i) {
flags = originalSourceMethod.arguments[i].modifiers;
}
result.add(javaEnvWithEcj().createBindingMethodParameter(this, arguments[i], name, flags, i));
}
return result;
});
}
@SuppressWarnings("squid:S1168") // return empty array instead of null
protected static char[] getParamName(MethodBinding b, AbstractMethodDeclaration sourceMethod, int paramIndex) {
if (b.parameterNames.length > paramIndex) {
return b.parameterNames[paramIndex];
}
if (sourceMethod != null && sourceMethod.arguments.length > paramIndex) {
return sourceMethod.arguments[paramIndex].name;
}
// if no parameter name info is in the class file
return null;
}
protected TypeVariableBinding[] getTypeVariables() {
//ask this or the actualType since we do not distinguish between the virtual parameterized type with arguments and the effective parameterized type with parameters
return SpiWithEcjUtils.nvl(m_binding.original(), m_binding).typeVariables();
}
@Override
public boolean hasTypeParameters() {
TypeVariableBinding[] typeVariables = getTypeVariables();
return typeVariables != null && typeVariables.length > 0;
}
@Override
public List<TypeParameterSpi> getTypeParameters() {
return m_typeParameters.computeIfAbsentAndGet(() -> SpiWithEcjUtils.createTypeParameters(this, getTypeVariables()));
}
@Override
public BindingTypeWithEcj getDeclaringType() {
return m_declaringType;
}
@Override
public boolean isConstructor() {
return m_binding.isConstructor();
}
@Override
public ISourceRange getSource() {
return m_source.computeIfAbsentAndGet(() -> {
AbstractMethodDeclaration decl = SpiWithEcjUtils.sourceMethodOf(m_binding);
if (decl == null) {
return null;
}
CompilationUnitSpi cu = m_declaringType.getCompilationUnit();
return javaEnvWithEcj().getSource(cu, decl.declarationSourceStart, decl.declarationSourceEnd);
});
}
@Override
public ISourceRange getSourceOfBody() {
return m_bodySource.computeIfAbsentAndGet(() -> {
AbstractMethodDeclaration decl = SpiWithEcjUtils.sourceMethodOf(m_binding);
if (decl == null) {
return null;
}
CompilationUnitSpi cu = m_declaringType.getCompilationUnit();
return javaEnvWithEcj().getSource(cu, decl.bodyStart, decl.bodyEnd);
});
}
@Override
public ISourceRange getJavaDoc() {
return m_javaDocSource.computeIfAbsentAndGet(() -> {
AbstractMethodDeclaration decl = SpiWithEcjUtils.sourceMethodOf(m_binding);
if (decl == null) {
return null;
}
return SpiWithEcjUtils.getJavaDocSource(decl.javadoc, m_declaringType, javaEnvWithEcj());
});
}
}