blob: d8aa9fd31dcc0d271c13e2959102ae0d12080d7c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.query.runtime.impl;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.acceleo.query.ast.Call;
import org.eclipse.acceleo.query.runtime.AcceleoQueryValidationException;
import org.eclipse.acceleo.query.runtime.ICompletionProposal;
import org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment;
import org.eclipse.acceleo.query.runtime.IValidationResult;
import org.eclipse.acceleo.query.runtime.impl.completion.JavaMethodServiceCompletionProposal;
import org.eclipse.acceleo.query.validation.type.ClassLiteralType;
import org.eclipse.acceleo.query.validation.type.ClassType;
import org.eclipse.acceleo.query.validation.type.EClassifierLiteralType;
import org.eclipse.acceleo.query.validation.type.EClassifierType;
import org.eclipse.acceleo.query.validation.type.IJavaType;
import org.eclipse.acceleo.query.validation.type.IType;
import org.eclipse.acceleo.query.validation.type.SequenceType;
import org.eclipse.acceleo.query.validation.type.SetType;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
/**
* Implementation of an {@link org.eclipse.acceleo.query.runtime.IService IService} for {@link Method}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
public class JavaMethodService extends AbstractService<Method> {
/**
* The {@link org.eclipse.acceleo.query.runtime.IService#getPriority() priority} for
* {@link JavaMethodService}.
*/
public static final int PRIORITY = 200;
/**
* The instance on which the service must be called.
*/
private final Object instance;
/**
* Known {@link IReadOnlyQueryEnvironment} to invalidate {@link JavaMethodService#returnTypes cached
* return types}.
*/
private IReadOnlyQueryEnvironment knwonEnvironment;
/**
* Return {@link IType} cache.
*/
private Set<IType> returnTypes;
/**
* Creates a new service instance given a method and an instance.
*
* @param method
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
public JavaMethodService(Method method, Object serviceInstance) {
super(method);
this.instance = serviceInstance;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IService#getName()
*/
@Override
public String getName() {
return getOrigin().getName();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IService#getParameterTypes(org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment)
*/
@Override
public List<IType> getParameterTypes(IReadOnlyQueryEnvironment queryEnvironment) {
final List<IType> result = new ArrayList<IType>();
for (Class<?> cls : getOrigin().getParameterTypes()) {
result.add(getClassType(queryEnvironment, cls));
}
return result;
}
/**
* Gets the {@link IJavaType} corresponding to the given {@link Type}.
*
* @param queryEnvironment
* the {@link IReadOnlyQueryEnvironment}
* @param type
* the {@link Type}
* @return the {@link IJavaType} corresponding to the given {@link Type}
*/
protected IJavaType getClassType(IReadOnlyQueryEnvironment queryEnvironment, Type type) {
final IJavaType result;
if (type instanceof ParameterizedType) {
final Class<?> cls = (Class<?>)((ParameterizedType)type).getRawType();
if (List.class.isAssignableFrom(cls)) {
final IType t = getClassType(queryEnvironment, ((ParameterizedType)type)
.getActualTypeArguments()[0]);
result = new SequenceType(queryEnvironment, t);
} else if (Set.class.isAssignableFrom(cls)) {
final IType t = getClassType(queryEnvironment, ((ParameterizedType)type)
.getActualTypeArguments()[0]);
result = new SetType(queryEnvironment, t);
} else {
result = new ClassType(queryEnvironment, cls);
}
} else if (type instanceof Class<?>) {
final Class<?> cls = (Class<?>)type;
result = new ClassType(queryEnvironment, cls);
} else {
result = new ClassType(queryEnvironment, Object.class);
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IService#getNumberOfParameters()
*/
@Override
public int getNumberOfParameters() {
return getOrigin().getParameterTypes().length;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.impl.AbstractService#internalInvoke(java.lang.Object[])
*/
@Override
protected Object internalInvoke(Object[] arguments) throws Exception {
return getOrigin().invoke(instance, arguments);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IService#getPriority()
*/
@Override
public int getPriority() {
return PRIORITY;
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
if (knwonEnvironment != queryEnvironment || returnTypes == null) {
knwonEnvironment = queryEnvironment;
returnTypes = new LinkedHashSet<IType>();
Type returnType = getOrigin().getGenericReturnType();
returnTypes.addAll(getIType(queryEnvironment, returnType));
}
return returnTypes;
}
/**
* Gets {@link IType} from a {@link Type}.
*
* @param queryEnvironment
* the {@link IReadOnlyQueryEnvironment}
* @param type
* the {@link Type}
* @return {@link IType} from a {@link Type}
* @see ValidationServices#getIType(Class)
*/
public Set<IType> getIType(IReadOnlyQueryEnvironment queryEnvironment, Type type) {
final Set<IType> result = new LinkedHashSet<IType>();
if (type instanceof ParameterizedType) {
final Class<?> cls = (Class<?>)((ParameterizedType)type).getRawType();
if (List.class.isAssignableFrom(cls)) {
for (IType t : getIType(queryEnvironment, ((ParameterizedType)type)
.getActualTypeArguments()[0])) {
result.add(new SequenceType(queryEnvironment, t));
}
} else if (Set.class.isAssignableFrom(cls)) {
for (IType t : getIType(queryEnvironment, ((ParameterizedType)type)
.getActualTypeArguments()[0])) {
result.add(new SetType(queryEnvironment, t));
}
} else {
result.add(new ClassType(queryEnvironment, cls));
}
} else if (type instanceof Class<?>) {
final Class<?> cls = (Class<?>)type;
result.addAll(getIType(queryEnvironment, cls));
} else {
result.add(new ClassType(queryEnvironment, Object.class));
}
return result;
}
/**
* Gets {@link IType} from a {@link Class}.
*
* @param queryEnvironment
* the {@link IReadOnlyQueryEnvironment}
* @param cls
* the {@link Class}
* @return {@link IType} from a {@link Class}
* @see ValidationServices#getIType(Type)
*/
private Set<IType> getIType(IReadOnlyQueryEnvironment queryEnvironment, Class<?> cls) {
final Set<IType> result = new LinkedHashSet<IType>();
if (List.class.isAssignableFrom(cls)) {
result.add(new SequenceType(queryEnvironment, new ClassType(queryEnvironment, Object.class)));
} else if (Set.class.isAssignableFrom(cls)) {
result.add(new SetType(queryEnvironment, new ClassType(queryEnvironment, Object.class)));
} else {
result.add(new ClassType(queryEnvironment, cls));
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.impl.AbstractService#matches(org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment,
* org.eclipse.acceleo.query.validation.type.IType[])
*/
@Override
public boolean matches(IReadOnlyQueryEnvironment queryEnvironment, IType[] argumentTypes) {
final ClassType[] classTypes = new ClassType[argumentTypes.length];
for (int i = 0; i < argumentTypes.length; ++i) {
Class<?> cls;
final IType iType = argumentTypes[i];
if (iType instanceof EClassifierLiteralType) {
cls = EClass.class;
} else if (iType instanceof EClassifierType) {
cls = queryEnvironment.getEPackageProvider().getClass(((EClassifierType)iType).getType());
if (cls == null) {
if (iType.getType() instanceof EClass) {
// instances of EClass are EObjects
cls = EObject.class;
} else {
// other instances can be anything
cls = Object.class;
}
}
} else if (iType instanceof ClassLiteralType) {
cls = Class.class;
} else if (iType instanceof IJavaType) {
cls = ((IJavaType)iType).getType();
} else {
throw new AcceleoQueryValidationException(iType.getClass().getCanonicalName());
}
if (cls != null) {
if ("boolean".equals(cls.getName())) {
cls = Boolean.class;
} else if ("int".equals(cls.getName())) {
cls = Integer.class;
} else if ("double".equals(cls.getName())) {
cls = Double.class;
}
}
classTypes[i] = new ClassType(queryEnvironment, cls);
}
return super.matches(queryEnvironment, classTypes);
}
/**
* Gets the {@link Object} instance.
*
* @return the {@link Object} instance if any, <code>null</code> otherwise
*/
public Object getInstance() {
return instance;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IService#getShortSignature()
*/
@Override
public String getShortSignature() {
return serviceShortSignature(getOrigin().getParameterTypes());
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IService#getLongSignature()
*/
@Override
public String getLongSignature() {
return getOrigin().toString();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return obj instanceof JavaMethodService && ((JavaMethodService)obj).getOrigin().equals(getOrigin());
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return getOrigin().hashCode();
}
@Override
public List<ICompletionProposal> getProposals(IReadOnlyQueryEnvironment queryEnvironment,
Set<IType> receiverTypes) {
final List<ICompletionProposal> result = new ArrayList<ICompletionProposal>();
result.add(new JavaMethodServiceCompletionProposal(this));
return result;
}
/**
* Creates a collection corresponding to this java method's {@link Method#getReturnType() return type}
* (either Sequence or Set) with the given type as collection type.
*
* @param queryEnvironment
* The query environment.
* @param collectionType
* The type of the content for the new collection type.
* @return The created collection, <code>collectionType</code> itself if the java method's
* {@link Method#getReturnType() return type} was not a collection.
*/
protected IType createReturnCollectionWithType(IReadOnlyQueryEnvironment queryEnvironment,
IType collectionType) {
IType result = collectionType;
if (List.class.isAssignableFrom(getOrigin().getReturnType())) {
result = new SequenceType(queryEnvironment, collectionType);
} else if (Set.class.isAssignableFrom(getOrigin().getReturnType())) {
result = new SetType(queryEnvironment, collectionType);
}
return result;
}
}