| /******************************************************************************* |
| * Copyright (c) 2020, 2021 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.aql.ls.services.textdocument; |
| |
| import java.lang.reflect.Method; |
| import java.net.URISyntaxException; |
| import java.util.Arrays; |
| import java.util.Objects; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.acceleo.ASTNode; |
| import org.eclipse.acceleo.Module; |
| import org.eclipse.acceleo.aql.location.AcceleoLocationLinkToAcceleo; |
| import org.eclipse.acceleo.aql.location.aql.AqlLocationLinkToAny; |
| import org.eclipse.acceleo.aql.location.aql.AqlLocationLinkToAql; |
| import org.eclipse.acceleo.aql.location.common.AbstractLocationLink; |
| import org.eclipse.acceleo.aql.ls.common.AcceleoLanguageServerPositionUtils; |
| import org.eclipse.acceleo.aql.parser.AcceleoAstResult; |
| import org.eclipse.acceleo.aql.parser.AcceleoAstUtils; |
| import org.eclipse.acceleo.aql.parser.AcceleoParser; |
| import org.eclipse.acceleo.query.ast.VariableDeclaration; |
| import org.eclipse.acceleo.query.runtime.IService; |
| import org.eclipse.acceleo.query.runtime.namespace.IQualifiedNameResolver; |
| import org.eclipse.acceleo.query.runtime.namespace.ISourceLocation; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.lsp4j.LocationLink; |
| import org.eclipse.lsp4j.Position; |
| import org.eclipse.lsp4j.Range; |
| |
| /** |
| * The part of the {@link AcceleoTextDocumentService} APIs that makes the link between |
| * {@link AbstractLocationLink} provided by the Acceleo API, and the {@link LocationLink} required by the |
| * LSP4J API. |
| * |
| * @author Florent Latombe |
| */ |
| public class AcceleoLocationLinkResolver { |
| |
| /** |
| * The owning {@link AcceleoTextDocumentService}. |
| */ |
| private final AcceleoTextDocumentService owner; |
| |
| /** |
| * Constructor. |
| * |
| * @param acceleoTextDocumentService |
| * the (non-{@code null}) owning {@link AcceleoTextDocumentService}. |
| */ |
| public AcceleoLocationLinkResolver(AcceleoTextDocumentService acceleoTextDocumentService) { |
| this.owner = Objects.requireNonNull(acceleoTextDocumentService); |
| } |
| |
| /** |
| * Transforms an {@link AbstractLocationLink} from Acceleo into a corresponding {@link LocationLink} for |
| * LSP4J. |
| * |
| * @param locationLinkToTransform |
| * the (non-{@code null}) {@link AbstractLocationLink} to transform. |
| * @return the {@link LocationLink} corresponding to {@code locationLink}. |
| */ |
| public LocationLink transform(AbstractLocationLink<?, ?> locationLinkToTransform) { |
| Objects.requireNonNull(locationLinkToTransform); |
| |
| final LocationLink locationLink; |
| // Dispatch depending on the concrete type of link. |
| if (locationLinkToTransform instanceof AcceleoLocationLinkToAcceleo) { |
| locationLink = this.transform((AcceleoLocationLinkToAcceleo)locationLinkToTransform); |
| } else if (locationLinkToTransform instanceof AqlLocationLinkToAql) { |
| locationLink = this.transform((AqlLocationLinkToAql)locationLinkToTransform); |
| } else if (locationLinkToTransform instanceof AqlLocationLinkToAny) { |
| locationLink = this.transform((AqlLocationLinkToAny)locationLinkToTransform); |
| } else { |
| throw new UnsupportedOperationException("Unsupported " + AbstractLocationLink.class |
| .getCanonicalName() + " implementation: " + locationLinkToTransform.toString()); |
| } |
| return locationLink; |
| } |
| |
| /** |
| * Transforms an {@link AcceleoLocationLinkToAcceleo} from Acceleo into a corresponding |
| * {@link LocationLink} for LSP4J. |
| * |
| * @param acceleoLocationLinkToAcceleo |
| * the (non-{@code null}) {@link AcceleoLocationLinkToAcceleo} to transform. |
| * @return the {@link LocationLink} corresponding to {@code acceleoLocationLinkToAcceleo}. |
| */ |
| private LocationLink transform(AcceleoLocationLinkToAcceleo acceleoLocationLinkToAcceleo) { |
| ASTNode linkOrigin = acceleoLocationLinkToAcceleo.getOrigin(); |
| AcceleoTextDocument originTextDocument = getAcceleoTextDocumentContaining(linkOrigin); |
| ASTNode linkOriginEquivalent = AcceleoAstUtils.getSelfOrEquivalentOf(linkOrigin, originTextDocument |
| .getAcceleoAstResult()); |
| |
| Range originSelectionRange = AcceleoLanguageServerPositionUtils.getRangeOf(linkOriginEquivalent, |
| originTextDocument.getAcceleoAstResult()); |
| |
| ASTNode destinationAcceleoNode = acceleoLocationLinkToAcceleo.getDestination(); |
| return this.createLocationLinkFromRangeToAcceleoDestination(originSelectionRange, |
| destinationAcceleoNode); |
| } |
| |
| /** |
| * Provides the {@link AcceleoTextDocument} containing the given Acceleo {@link ASTNode}. |
| * |
| * @param astNode |
| * the (non-{@code null}) {@link ASTNode}. |
| * @return the {@link AcceleoTextDocument} whose contents define the {@link Module} defining |
| * {@code astNode}. {@code null} if it could not be determined. |
| */ |
| private AcceleoTextDocument getAcceleoTextDocumentContaining(ASTNode astNode) { |
| Module containingModule = AcceleoAstUtils.getContainerModule(astNode); |
| AcceleoTextDocument containingTextDocument = this.getTextDocumentDefining(containingModule); |
| return containingTextDocument; |
| } |
| |
| /** |
| * Provides the {@link AcceleoTextDocument} that defines the given Acceleo {@link Module}. |
| * |
| * @param acceleoModule |
| * the (non-{@code null}) {@link Module} we want to find the defining document of. |
| * @return the {@link AcceleoTextDocument} that defines {@code acceleoModule}. {@code null} if it could |
| * not be determined. |
| */ |
| private AcceleoTextDocument getTextDocumentDefining(Module acceleoModule) { |
| return this.owner.findTextDocumentDefining(acceleoModule); |
| } |
| |
| /** |
| * Transforms an {@link AqlLocationLinkToAql} from AQL into a corresponding {@link LocationLink} for |
| * LSP4J. |
| * |
| * @param aqlLocationLinkToAql |
| * the (non-{@code null}) {@link AqlLocationLinkToAql} to transform. |
| * @return the {@link LocationLink} corresponding to {@code aqlLocationLinkToAql}. |
| */ |
| private LocationLink transform(AqlLocationLinkToAql aqlLocationLinkToAql) { |
| EObject linkOrigin = aqlLocationLinkToAql.getOrigin(); |
| Range originSelectionRange = getRangeForAqlAstElement(linkOrigin); |
| |
| EObject linkDestination = aqlLocationLinkToAql.getDestination(); |
| return createLocationLinkFromRangeToAqlDestination(originSelectionRange, linkDestination); |
| } |
| |
| /** |
| * Provides the "selection range" of an {@link EObject AQL AST element} |
| * ({@link org.eclipse.acceleo.query.ast.Expression} or {@link VariableDeclaration}). |
| * |
| * @param aqlAstElement |
| * the (non-{@code null}) AQL {@link org.eclipse.acceleo.query.ast.Expression} or |
| * {@link VariableDeclaration}. |
| * @return the {@link Range} corresponding to {@code aqlAstElement}. |
| */ |
| private Range getRangeForAqlAstElement(EObject aqlAstElement) { |
| final ASTNode acceleoNodeContainingLinkOrigin = getAcceleoAstNodeContainingAqlElement(aqlAstElement); |
| AcceleoTextDocument originTextDocument = getAcceleoTextDocumentContaining( |
| acceleoNodeContainingLinkOrigin); |
| |
| Range originSelectionRange; |
| if (aqlAstElement instanceof org.eclipse.acceleo.query.ast.Expression) { |
| org.eclipse.acceleo.query.ast.Expression originAqlExpression = (org.eclipse.acceleo.query.ast.Expression)aqlAstElement; |
| org.eclipse.acceleo.query.ast.Expression originAqlExpressionEquivalent = AcceleoAstUtils |
| .getSelfOrEquivalentOf(originAqlExpression, originTextDocument.getAcceleoAstResult()); |
| originSelectionRange = AcceleoLanguageServerPositionUtils.getIdentifierRangeOf( |
| originAqlExpressionEquivalent, originTextDocument.getAcceleoAstResult()); |
| } else if (aqlAstElement instanceof org.eclipse.acceleo.query.ast.VariableDeclaration) { |
| org.eclipse.acceleo.query.ast.VariableDeclaration originAqlVariableDeclaration = (org.eclipse.acceleo.query.ast.VariableDeclaration)aqlAstElement; |
| org.eclipse.acceleo.query.ast.VariableDeclaration originAqlVariableDeclarationEquivalent = AcceleoAstUtils |
| .getSelfOrEquivalentOf(originAqlVariableDeclaration, originTextDocument |
| .getAcceleoAstResult()); |
| originSelectionRange = AcceleoLanguageServerPositionUtils.getIdentifierRangeOf( |
| originAqlVariableDeclarationEquivalent, originTextDocument.getAcceleoAstResult()); |
| } else { |
| // This should never happen. |
| throw new IllegalStateException( |
| "Expected link origin to be either an AQL Expression or an AQL Variable Declaration but it was: " |
| + aqlAstElement.toString() + "."); |
| } |
| return originSelectionRange; |
| } |
| |
| /** |
| * Provides the {@link ASTNode} containing the given node from an AQL AST. |
| * |
| * @param aqlElement |
| * the (non-{@code null}) AQL element ({@link org.eclipse.acceleo.query.ast.Expression} or |
| * {@link org.eclipse.acceleo.query.ast.VariableDeclaration}). |
| * @return the {@link ASTNode} containing {@code aqlElement}. |
| */ |
| private static ASTNode getAcceleoAstNodeContainingAqlElement(EObject aqlElement) { |
| final ASTNode acceleoNodeContainingAqlElement; |
| if (aqlElement instanceof org.eclipse.acceleo.query.ast.Expression) { |
| org.eclipse.acceleo.query.ast.Expression originAqlExpression = (org.eclipse.acceleo.query.ast.Expression)aqlElement; |
| acceleoNodeContainingAqlElement = AcceleoAstUtils.getContainerOfAqlAstElement( |
| originAqlExpression); |
| } else if (aqlElement instanceof org.eclipse.acceleo.query.ast.VariableDeclaration) { |
| org.eclipse.acceleo.query.ast.VariableDeclaration originAqlVariableDeclaration = (org.eclipse.acceleo.query.ast.VariableDeclaration)aqlElement; |
| acceleoNodeContainingAqlElement = AcceleoAstUtils.getContainerOfAqlAstElement( |
| originAqlVariableDeclaration); |
| } else { |
| // This should never happen. |
| throw new IllegalStateException( |
| "Expected AQL element to be either an AQL Expression or an AQL Variable Declaration but it was: " |
| + aqlElement.toString() + "."); |
| } |
| return acceleoNodeContainingAqlElement; |
| } |
| |
| /** |
| * Transforms an {@link AqlLocationLinkToAny} from AQL into a corresponding {@link LocationLink} for |
| * LSP4J. |
| * |
| * @param aqlLocationLinkToAny |
| * the (non-{@code null}) {@link AqlLocationLinkToAny} to transform. |
| * @return the {@link LocationLink} corresponding to {@code aqlLocationLinkToAny}. |
| */ |
| private LocationLink transform(AqlLocationLinkToAny aqlLocationLinkToAny) { |
| EObject aqlOrigin = aqlLocationLinkToAny.getOrigin(); |
| Range originSelectionRange = getRangeForAqlAstElement(aqlOrigin); |
| Object destination = aqlLocationLinkToAny.getDestination(); |
| return createLocationLinkFromRangeToAnyDestination(aqlOrigin, originSelectionRange, destination); |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} to a destination of undetermined nature. |
| * |
| * @param linkOrigin |
| * the (non-{@code null}) origin {@link EObject} of the link to create. |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range} of the link. |
| * @param destination |
| * the (non-{@code null}) destination of the link. |
| * @return the {@link LocationLink} from that points from {@code originSelectionRange} to |
| * {@code destination}. |
| */ |
| private LocationLink createLocationLinkFromRangeToAnyDestination(EObject linkOrigin, |
| Range originSelectionRange, Object destination) { |
| LocationLink locationLink; |
| if (destination instanceof ASTNode) { |
| ASTNode destinationNode = (ASTNode)destination; |
| locationLink = createLocationLinkFromRangeToAcceleoDestination(originSelectionRange, |
| destinationNode); |
| } else if (destination instanceof org.eclipse.acceleo.query.ast.Expression |
| || destination instanceof org.eclipse.acceleo.query.ast.VariableDeclaration) { |
| // This should probably not happen due to how the relation between Acceleo and AQL is structured. |
| // i.e. both Acceleo and AQL are aware of AQL so links with an AQL destination should not be |
| // represented as links to "any" destination. |
| // Still, support it just in case... |
| locationLink = createLocationLinkFromRangeToAqlDestination(originSelectionRange, |
| (EObject)destination); |
| } else if (destination instanceof IService<?>) { |
| locationLink = createLocationLinkFromRangeToServiceDestination(linkOrigin, originSelectionRange, |
| (IService<?>)destination); |
| } else if (destination instanceof java.lang.Class<?>) { |
| locationLink = createLocationLinkFromRangeToJavaClassDestination(linkOrigin, originSelectionRange, |
| (Class<?>)destination); |
| } else if (destination instanceof Method) { |
| locationLink = createLocationLinkFromRangeToJavaMethodDestination(linkOrigin, |
| originSelectionRange, (Method)destination); |
| } else if (destination instanceof EObject) { |
| locationLink = createLocationLinkFromRangeToEObjectDestination(linkOrigin, originSelectionRange, |
| (EObject)destination); |
| } else { |
| // Implement more cases if we have links that link to other types of destination: maybe Java? |
| // This depends on how we have bound variables in the environment passed to the AQL evaluator. |
| throw new IllegalStateException("Trying to create a link to destination: " + destination |
| + " but this type of destination is not supported."); |
| } |
| return locationLink; |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} that points to the given AQL element. |
| * |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range} of the created link. |
| * @param aqlDestination |
| * the (non-{@code null}) destination {@link EObject} that must be an AQL element |
| * ({@link org.eclipse.acceleo.query.ast.Expression} or {@link VariableDeclaration}). |
| * @return the created {@link LocationLink}. |
| */ |
| private LocationLink createLocationLinkFromRangeToAqlDestination(Range originSelectionRange, |
| EObject aqlDestination) { |
| final ASTNode acceleoNodeContainingLinkDestination = getAcceleoAstNodeContainingAqlElement( |
| aqlDestination); |
| AcceleoTextDocument destinationTextDocument = getAcceleoTextDocumentContaining( |
| acceleoNodeContainingLinkDestination); |
| |
| // Link target parameters. |
| String targetDocumentUri = destinationTextDocument.getUri().toString(); |
| final Range targetRange = getRangeForAqlAstElement(aqlDestination); |
| // FIXME: we probably only want to select part of the target. |
| Range targetSelectionRange = targetRange; |
| |
| LocationLink locationLink = new LocationLink(targetDocumentUri, targetRange, targetSelectionRange, |
| originSelectionRange); |
| return locationLink; |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} to the given Acceleo {@link ASTNode} |
| * destination. |
| * |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range}. |
| * @param destinationNode |
| * the (non-{@code null}) destination {@link ASTNode}. |
| * @return the {@link LocationLink} from {@code originSelectionRange} to {@code destinationNode}. |
| */ |
| private LocationLink createLocationLinkFromRangeToAcceleoDestination(Range originSelectionRange, |
| ASTNode destinationNode) { |
| AcceleoTextDocument destinationTextDocument = this.getAcceleoTextDocumentContaining(destinationNode); |
| if (destinationTextDocument == null) { |
| // This should never happen because we a transforming a link that was provided by the |
| // AcceleoLocator, so if a link destination could not be determined then the locator would not |
| // have returned a link. |
| throw new IllegalArgumentException("Could not find the Acceleo document that defines " |
| + destinationNode.toString()); |
| } |
| AcceleoAstResult destinationAcceleoAstResult = destinationTextDocument.getAcceleoAstResult(); |
| |
| // Note: the destination ASTNode comes from a parsing that is not necessarily the one known by the |
| // destination text document, therefore we have to be able to locate the equivalent of an ASTNode in |
| // another AcceleoAstResult of the same Acceleo file. |
| ASTNode destinationNodeInDestinationTextDocument = AcceleoAstUtils.getSelfOrEquivalentOf( |
| destinationNode, destinationAcceleoAstResult); |
| Range targetRange = AcceleoLanguageServerPositionUtils.getRangeOf( |
| destinationNodeInDestinationTextDocument, destinationAcceleoAstResult); |
| // FIXME: we probably only want to select part of the target. |
| Range targetSelectionRange = AcceleoLanguageServerPositionUtils.getIdentifierRangeOf( |
| destinationNodeInDestinationTextDocument, destinationTextDocument.getAcceleoAstResult()); |
| |
| // Link target parameters. |
| Module destinationModule = destinationTextDocument.getAcceleoAstResult().getModule(); |
| String qualifiedName = URI.decode(destinationModule.eResource().getURI().toString().replaceFirst( |
| AcceleoParser.ACCELEOENV_URI_PROTOCOL, "")); |
| |
| // TODO this is a more general matter, it should be performed in the AcceleoWorkspace |
| // open source file whenever it's possible |
| java.net.URI targetDocumentUri; |
| try { |
| final IQualifiedNameResolver resolver = destinationTextDocument.getQueryEnvironment() |
| .getLookupEngine().getResolver(); |
| targetDocumentUri = resolver.getSourceURL(qualifiedName).toURI(); |
| } catch ( |
| |
| URISyntaxException e) { |
| targetDocumentUri = null; |
| } |
| if (targetDocumentUri == null) { |
| targetDocumentUri = destinationTextDocument.getUri(); |
| } |
| |
| LocationLink locationLink = new LocationLink(targetDocumentUri.toString(), targetRange, |
| targetSelectionRange, originSelectionRange); |
| return locationLink; |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} to the given Java {@link Class} |
| * destination. |
| * |
| * @param linkOrigin |
| * the (non-{@code null}) origin {@link EObject} of the link. |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range}. |
| * @param destinationJavaClass |
| * the (non-{@code null}) destination Java {@link Class}. |
| * @return the {@link LocationLink} from {@code originSelectionRange} to {@code destinationJavaClass}. |
| */ |
| private LocationLink createLocationLinkFromRangeToJavaClassDestination(EObject linkOrigin, |
| Range originSelectionRange, Class<?> destinationJavaClass) { |
| ASTNode acceleoAstNode = getAcceleoAstNodeContainingAqlElement(linkOrigin); |
| AcceleoTextDocument acceleoTextDocument = this.getAcceleoTextDocumentContaining(acceleoAstNode); |
| |
| // TODO: implement link to Java sources. |
| // We probably want to retrieve the Java context (classpath, etc.) of the AcceleoTextDocument, and use |
| // it to retrieve the location of the Java class. Most of this mechanism should be implemented for the |
| // resolution of modules anyway. |
| |
| // FIXME: temporary placeholder so it still sort of works |
| String urlPrefix = "https://docs.oracle.com/javase/8/docs/api/"; |
| String urlPostfix = ".html"; |
| String classNameUrlPart = destinationJavaClass.getCanonicalName().replace('.', '/'); |
| String urlToJavadoc = urlPrefix + classNameUrlPart + urlPostfix; |
| |
| String targetUri = urlToJavadoc; |
| Range targetRange = new Range(new Position(0, 0), new Position(0, 0)); |
| Range targetSelectionRange = targetRange; |
| |
| LocationLink locationLink = new LocationLink(targetUri, targetRange, targetSelectionRange, |
| originSelectionRange); |
| return locationLink; |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} to the given {@link IService} destination. |
| * |
| * @param linkOrigin |
| * the (non-{@code null}) origin {@link EObject} of the link. |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range}. |
| * @param destinationService |
| * the (non-{@code null}) destination {@link IService}. |
| * @return the {@link LocationLink} from {@code originSelectionRange} to {@code destinationService}. |
| */ |
| private LocationLink createLocationLinkFromRangeToServiceDestination(EObject linkOrigin, |
| Range originSelectionRange, IService<?> destinationService) { |
| ASTNode acceleoAstNode = getAcceleoAstNodeContainingAqlElement(linkOrigin); |
| AcceleoTextDocument acceleoTextDocument = this.getAcceleoTextDocumentContaining(acceleoAstNode); |
| |
| final IQualifiedNameResolver resolver = acceleoTextDocument.getQueryEnvironment().getLookupEngine() |
| .getResolver(); |
| |
| final ISourceLocation sourceLocation = resolver.getSourceLocation(destinationService); |
| final LocationLink locationLink; |
| if (sourceLocation != null) { |
| Position targetRangeStart = new Position(sourceLocation.getRange().getStart().getLine(), |
| sourceLocation.getRange().getStart().getColumn()); |
| Position targetRangeEnd = new Position(sourceLocation.getRange().getEnd().getLine(), |
| sourceLocation.getRange().getEnd().getColumn()); |
| Range targetRange = new Range(targetRangeStart, targetRangeEnd); |
| |
| Position targetSelectionRangeStart = new Position(sourceLocation.getIdentifierRange().getStart() |
| .getLine(), sourceLocation.getIdentifierRange().getStart().getColumn()); |
| Position targetSelectionRangeEnd = new Position(sourceLocation.getIdentifierRange().getEnd() |
| .getLine(), sourceLocation.getIdentifierRange().getEnd().getColumn()); |
| Range targetSelectionRange = new Range(targetSelectionRangeStart, targetSelectionRangeEnd); |
| |
| locationLink = new LocationLink(sourceLocation.getSourceURL().toString(), targetRange, |
| targetSelectionRange, originSelectionRange); |
| } else { |
| locationLink = createLocationLinkFromRangeToAnyDestination(linkOrigin, originSelectionRange, |
| destinationService.getOrigin()); |
| } |
| |
| return locationLink; |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} to the given Java {@link Method} |
| * destination. |
| * |
| * @param linkOrigin |
| * the (non-{@code null}) origin {@link EObject} of the link. |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range}. |
| * @param destinationJavaMethod |
| * the (non-{@code null}) destination Java {@link Method}. |
| * @return the {@link LocationLink} from {@code originSelectionRange} to {@code destinationJavaMethod}. |
| */ |
| private LocationLink createLocationLinkFromRangeToJavaMethodDestination(EObject linkOrigin, |
| Range originSelectionRange, Method destinationJavaMethod) { |
| ASTNode acceleoAstNode = getAcceleoAstNodeContainingAqlElement(linkOrigin); |
| AcceleoTextDocument acceleoTextDocument = this.getAcceleoTextDocumentContaining(acceleoAstNode); |
| |
| // TODO: implement link to Java sources. |
| // We probably want to retrieve the Java context (classpath, etc.) of the AcceleoTextDocument, and use |
| // it to retrieve the location of the Java method. Most of this mechanism should be implemented for |
| // the |
| // resolution of modules anyway. |
| |
| // FIXME: temporary placeholder so it still sort of works |
| String urlPrefix = "https://docs.oracle.com/javase/8/docs/api/"; |
| String urlPostfix = ".html"; |
| String classNameUrlPart = destinationJavaMethod.getDeclaringClass().getCanonicalName().replace('.', |
| '/'); |
| String methodNameUrlPart = destinationJavaMethod.getName(); |
| String methodParametersUrlPart = Arrays.asList(destinationJavaMethod.getParameterTypes()).stream() |
| .map(parameterType -> parameterType.getSimpleName()).collect(Collectors.joining("-")); |
| String urlToJavadoc = urlPrefix + classNameUrlPart + urlPostfix + "#" + methodNameUrlPart + "-" |
| + methodParametersUrlPart; |
| |
| String targetUri = urlToJavadoc; |
| Range targetRange = new Range(new Position(0, 0), new Position(0, 0)); |
| Range targetSelectionRange = targetRange; |
| |
| LocationLink locationLink = new LocationLink(targetUri, targetRange, targetSelectionRange, |
| originSelectionRange); |
| return locationLink; |
| } |
| |
| /** |
| * Creates a {@link LocationLink} from the given {@link Range} to the given EMF {@link EOperation} |
| * destination. |
| * |
| * @param linkOrigin |
| * the (non-{@code null}) origin {@link EObject} of the link. |
| * @param originSelectionRange |
| * the (non-{@code null}) origin selection {@link Range}. |
| * @param destinationEObject |
| * the (non-{@code null}) destination EMF {@link EObject}. |
| * @return the {@link LocationLink} from {@code originSelectionRange} to {@code destinationEObject}. |
| */ |
| private LocationLink createLocationLinkFromRangeToEObjectDestination(EObject linkOrigin, |
| Range originSelectionRange, EObject destinationEObject) { |
| ASTNode acceleoAstNode = getAcceleoAstNodeContainingAqlElement(linkOrigin); |
| AcceleoTextDocument acceleoTextDocument = this.getAcceleoTextDocumentContaining(acceleoAstNode); |
| |
| // TODO: implement link to EMF metamodel elements. |
| // We probably want to retrieve the Java/EMF context (classpath, etc.) of the AcceleoTextDocument, and |
| // use |
| // it to retrieve the location of the EMF operation. Most of this mechanism should be implemented for |
| // the |
| // resolution of modules anyway. |
| |
| // FIXME: temporary placeholder so it still sort of works |
| String url = EcoreUtil.getURI(destinationEObject).toString(); |
| |
| String targetUri = url; |
| Range targetRange = new Range(new Position(0, 0), new Position(0, 0)); |
| Range targetSelectionRange = targetRange; |
| |
| LocationLink locationLink = new LocationLink(targetUri, targetRange, targetSelectionRange, |
| originSelectionRange); |
| return locationLink; |
| } |
| } |