blob: c83451d92e61c5a6e3af1c4fea0b76c9e013af4d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2015 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
* Stephan Herrmann - Contribution for
* Bug 458613 - [1.8] lambda not shown in quick type hierarchy
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.Substitution;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.core.util.MementoTokenizer;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
public class LambdaExpression extends SourceType {
SourceTypeElementInfo elementInfo;
LambdaMethod lambdaMethod;
// These fields could be materialized from elementInfo, but for ease of use stashed here
protected int sourceStart;
protected int sourceEnd;
protected int arrowPosition;
protected String interphase;
// Construction from AST node
LambdaExpression(JavaElement parent, org.eclipse.jdt.internal.compiler.ast.LambdaExpression lambdaExpression) {
super(parent, new String(CharOperation.NO_CHAR));
this.sourceStart = lambdaExpression.sourceStart;
this.sourceEnd = lambdaExpression.sourceEnd;
this.arrowPosition = lambdaExpression.arrowPosition;
TypeBinding supertype = findLambdaSuperType(lambdaExpression);
this.interphase = new String(CharOperation.replaceOnCopy(supertype.genericTypeSignature(), '/', '.'));
this.elementInfo = makeTypeElementInfo(this, this.interphase, this.sourceStart, this.sourceEnd, this.arrowPosition);
this.lambdaMethod = LambdaFactory.createLambdaMethod(this, lambdaExpression);
this.elementInfo.children = new IJavaElement[] { this.lambdaMethod };
}
public TypeBinding findLambdaSuperType(org.eclipse.jdt.internal.compiler.ast.LambdaExpression lambdaExpression) {
// start from the specific type, ignoring type arguments:
TypeBinding original = lambdaExpression.resolvedType.original();
// infer type arguments from here:
final TypeBinding descType = lambdaExpression.descriptor.declaringClass;
if (descType instanceof ParameterizedTypeBinding) {
final ParameterizedTypeBinding descPTB = (ParameterizedTypeBinding) descType;
// intermediate type: original pulled up to the level of descType:
final TypeBinding originalSuper = original.findSuperTypeOriginatingFrom(descType);
return Scope.substitute(new Substitution() {
@Override
public TypeBinding substitute(TypeVariableBinding typeVariable) {
if (originalSuper instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding originalSuperPTB = (ParameterizedTypeBinding) originalSuper;
TypeBinding[] superArguments = originalSuperPTB.arguments;
for (int i = 0; i < superArguments.length; i++) {
// if originalSuper holds typeVariable as it i'th argument, then the i'th argument of descType is our answer:
if (TypeBinding.equalsEquals(superArguments[i], typeVariable))
return descPTB.arguments[i];
}
}
// regular substitution:
return descPTB.substitute(typeVariable);
}
@Override
public boolean isRawSubstitution() {
return descPTB.isRawType();
}
@Override
public LookupEnvironment environment() {
return descPTB.environment;
}
//{ObjectTeams:
@Override
public ITeamAnchor substituteAnchor(ITeamAnchor anchor, int rank) {
return descPTB.substituteAnchor(anchor, rank);
}
// SH}
}, original);
}
return original;
}
// Construction from memento
LambdaExpression(JavaElement parent, String interphase, int sourceStart, int sourceEnd, int arrowPosition) {
super(parent, new String(CharOperation.NO_CHAR));
this.sourceStart = sourceStart;
this.sourceEnd = sourceEnd;
this.arrowPosition = arrowPosition;
this.interphase = interphase;
this.elementInfo = makeTypeElementInfo(this, interphase, this.sourceStart = sourceStart, sourceEnd, arrowPosition);
// Method is in the process of being fabricated, will be attached shortly.
}
// Construction from subtypes.
LambdaExpression(JavaElement parent, String interphase, int sourceStart, int sourceEnd, int arrowPosition, LambdaMethod lambdaMethod) {
super(parent, new String(CharOperation.NO_CHAR));
this.sourceStart = sourceStart;
this.sourceEnd = sourceEnd;
this.arrowPosition = arrowPosition;
this.interphase = interphase;
this.elementInfo = makeTypeElementInfo(this, interphase, this.sourceStart = sourceStart, sourceEnd, arrowPosition);
this.elementInfo.children = new IJavaElement[] { this.lambdaMethod = lambdaMethod };
}
// Lambda expression is not backed by model, fabricate element information structure and stash it.
static private SourceTypeElementInfo makeTypeElementInfo (LambdaExpression handle, String interphase, int sourceStart, int sourceEnd, int arrowPosition) {
SourceTypeElementInfo elementInfo = new SourceTypeElementInfo();
elementInfo.setFlags(0);
elementInfo.setHandle(handle);
elementInfo.setSourceRangeStart(sourceStart);
elementInfo.setSourceRangeEnd(sourceEnd);
elementInfo.setNameSourceStart(sourceStart);
elementInfo.setNameSourceEnd(arrowPosition);
elementInfo.setSuperclassName(null);
elementInfo.addCategories(handle, null);
JavaModelManager manager = JavaModelManager.getJavaModelManager();
char[][] superinterfaces = new char [][] { manager.intern(Signature.toString(interphase).toCharArray()) }; // drops marker interfaces - to fix.
elementInfo.setSuperInterfaceNames(superinterfaces);
return elementInfo;
}
protected void closing(Object info) throws JavaModelException {
// nothing to do, not backed by model ATM.
}
public boolean equals(Object o) {
if (this == o)
return true;
/* I see cases where equal lambdas are dismissed as unequal on account of working copy owner.
This results in spurious failures. See JavaSearchBugs8Tests.testBug400905_0021()
For now exclude the working copy owner and compare
*/
if (o instanceof LambdaExpression) {
LambdaExpression that = (LambdaExpression) o;
if (this.sourceStart != that.sourceStart)
return false;
ITypeRoot thisTR = this.getTypeRoot();
ITypeRoot thatTR = that.getTypeRoot();
return thisTR.getElementName().equals(thatTR.getElementName()) && thisTR.getParent().equals(thatTR.getParent());
}
return false;
}
public int hashCode() {
return Util.combineHashCodes(super.hashCode(), this.sourceStart);
}
public Object getElementInfo(IProgressMonitor monitor) throws JavaModelException {
return this.elementInfo;
}
protected char getHandleMementoDelimiter() {
return JavaElement.JEM_LAMBDA_EXPRESSION;
}
/*
* @see JavaElement#getHandleMemento(StringBuffer)
*/
protected void getHandleMemento(StringBuffer buff) {
getHandleMemento(buff, true, true);
// lambda method and lambda expression cannot share the same memento - add a trailing discriminator.
appendEscapedDelimiter(buff, getHandleMementoDelimiter());
}
protected void getHandleMemento(StringBuffer buff, boolean serializeParent, boolean serializeChild) {
if (serializeParent)
((JavaElement)getParent()).getHandleMemento(buff);
appendEscapedDelimiter(buff, getHandleMementoDelimiter());
appendEscapedDelimiter(buff, JEM_STRING);
escapeMementoName(buff, this.interphase);
buff.append(JEM_COUNT);
buff.append(this.sourceStart);
buff.append(JEM_COUNT);
buff.append(this.sourceEnd);
buff.append(JEM_COUNT);
buff.append(this.arrowPosition);
if (serializeChild)
this.lambdaMethod.getHandleMemento(buff, false);
}
public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner workingCopyOwner) {
if (token.charAt(0) != JEM_LAMBDA_METHOD)
return null;
// ----
if (!memento.hasMoreTokens()) return this;
String selector = memento.nextToken();
if (!memento.hasMoreTokens() || memento.nextToken().charAt(0) != JEM_COUNT) return this;
if (!memento.hasMoreTokens()) return this;
int length = Integer.parseInt(memento.nextToken());
String [] parameterTypes = new String[length];
String [] parameterNames = new String[length];
for (int i = 0; i < length; i++) {
if (!memento.hasMoreTokens() || memento.nextToken().charAt(0) != JEM_STRING) return this;
parameterTypes[i] = memento.nextToken();
if (!memento.hasMoreTokens() || memento.nextToken().charAt(0) != JEM_STRING) return this;
parameterNames[i] = memento.nextToken();
}
if (!memento.hasMoreTokens() || memento.nextToken().charAt(0) != JEM_STRING) return this;
String returnType = memento.nextToken();
if (!memento.hasMoreTokens() || memento.nextToken().charAt(0) != JEM_STRING) return this;
String key = memento.nextToken();
this.lambdaMethod = LambdaFactory.createLambdaMethod(this, selector, key, this.sourceStart, this.sourceEnd, this.arrowPosition, parameterTypes, parameterNames, returnType);
ILocalVariable [] parameters = new ILocalVariable[length];
for (int i = 0; i < length; i++) {
parameters[i] = (ILocalVariable) this.lambdaMethod.getHandleFromMemento(memento, workingCopyOwner);
}
this.lambdaMethod.elementInfo.arguments = parameters;
this.elementInfo.children = new IJavaElement[] { this.lambdaMethod };
if (!memento.hasMoreTokens())
return this.lambdaMethod;
switch (memento.nextToken().charAt(0)) {
case JEM_LAMBDA_METHOD:
if (!memento.hasMoreTokens())
return this.lambdaMethod;
return this.lambdaMethod.getHandleFromMemento(memento, workingCopyOwner);
case JEM_LAMBDA_EXPRESSION:
default:
return this;
}
}
public IJavaElement[] getChildren() throws JavaModelException {
return new IJavaElement[] { this.lambdaMethod };
}
public boolean isLocal() {
return true;
}
public JavaElement resolved(Binding binding) {
ResolvedLambdaExpression resolvedHandle = new ResolvedLambdaExpression(this.parent, this, new String(binding.computeUniqueKey()));
return resolvedHandle;
}
public IMethod getMethod() {
return this.lambdaMethod;
}
@Override
public boolean isLambda() {
return true;
}
@Override
public boolean isAnonymous() {
return false;
}
public void toStringName(StringBuffer buffer) {
super.toStringName(buffer);
buffer.append("<lambda #"); //$NON-NLS-1$
buffer.append(this.occurrenceCount);
buffer.append(">"); //$NON-NLS-1$
}
@Override
public IJavaElement getPrimaryElement(boolean checkOwner) {
if (checkOwner) {
CompilationUnit cu = (CompilationUnit)getAncestor(COMPILATION_UNIT);
if (cu == null || cu.isPrimary()) return this;
}
IJavaElement primaryParent = this.parent.getPrimaryElement(false);
if (primaryParent instanceof JavaElement) {
JavaElement ancestor = (JavaElement) primaryParent;
StringBuffer buffer = new StringBuffer(32);
getHandleMemento(buffer, false, true);
String memento = buffer.toString();
return ancestor.getHandleFromMemento(new MementoTokenizer(memento), DefaultWorkingCopyOwner.PRIMARY).getParent();
}
return this;
}
public String[] getSuperInterfaceTypeSignatures() throws JavaModelException {
return new String[] { this.interphase };
}
}