/******************************************************************************* * Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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 *******************************************************************************/ package de.gebit.integrity.ui.contentassist; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jface.internal.text.html.BrowserInformationControl; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Shell; import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal; import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext; import com.google.inject.Provider; import de.gebit.integrity.dsl.CallDefinition; import de.gebit.integrity.dsl.PackageDefinition; import de.gebit.integrity.dsl.SuiteDefinition; import de.gebit.integrity.dsl.TestDefinition; import de.gebit.integrity.dsl.VariableOrConstantEntity; import de.gebit.integrity.utils.IntegrityDSLUtil; /** * A context-aware configurable completion proposal. This proposal knows its content assist context and uses this in * order to resolve the EObject proxy if necessary on request of additional proposal info, but only if the fully * resolved object is necessary to display certain content assist info later (via * {@link de.gebit.integrity.ui.documentation.IntegrityEObjectDocumentationProvider}). * * @author Rene Schneider - initial API and implementation * */ @SuppressWarnings("restriction") public class IntegrityConfigurableCompletionProposal extends ConfigurableCompletionProposal implements ICompletionProposalExtension3 { /** * The context. */ private ContentAssistContext context; /** * This is set if this proposal is suggesting a variable defined in a suite definition header (that is, a suite * parameter). These are subject to a few specialties when it comes to proposals and scopes. If I one day find out * how to model this construction a little more elegant, I'll do it. Until then this hack should do the job ;-). */ private SuiteDefinition suiteDefiningProposedParameter; /** * Whether to use a HTML browser window to display the proposal info, if possible. */ private boolean useHtmlAdditionalProposalInfo; /** * The additional proposal info object. Stored here because the superclass one is private. */ private Object additionalProposalInfoObject; /** * The resolved {@link #additionalProposalInfoObject} is cached here. */ private EObject resolvedAdditionalProposalInfoObject; /** * The context resource. Stored here because the superclass one is private. */ private Resource proposalContextResource; public void setUseHtmlAdditionalProposalInfo(boolean aUseHtmlAdditionalProposalInfoFlag) { this.useHtmlAdditionalProposalInfo = aUseHtmlAdditionalProposalInfoFlag; } /** * Creates a new instance. * * @param aReplacementString * @param aReplacementOffset * @param aReplacementLength * @param aCursorPosition * @param anImage * @param aDisplayString * @param aContextInformation * @param anAdditionalProposalInfo */ // SUPPRESS CHECKSTYLE ParameterNumber public IntegrityConfigurableCompletionProposal(String aReplacementString, int aReplacementOffset, int aReplacementLength, int aCursorPosition, Image anImage, StyledString aDisplayString, IContextInformation aContextInformation, ContentAssistContext aContext) { super(aReplacementString, aReplacementOffset, aReplacementLength, aCursorPosition, anImage, aDisplayString, aContextInformation, null); context = aContext; } @Override public void setAdditionalProposalInfo(final Object anAdditionalProposalInfo) { Object tempAdditionalProposalInfo = anAdditionalProposalInfo; if (anAdditionalProposalInfo instanceof EObject && ((EObject) anAdditionalProposalInfo).eIsProxy() && requiresResolvingForContentAssist((EObject) anAdditionalProposalInfo)) { // resolve the proxy before continuing tempAdditionalProposalInfo = new Provider<EObject>() { @Override public EObject get() { return EcoreUtil.resolve((EObject) anAdditionalProposalInfo, context.getResource()); } }.get(); } if (tempAdditionalProposalInfo instanceof VariableOrConstantEntity && ((VariableOrConstantEntity) tempAdditionalProposalInfo).eContainer() instanceof SuiteDefinition) { suiteDefiningProposedParameter = (SuiteDefinition) ((VariableOrConstantEntity) tempAdditionalProposalInfo) .eContainer(); // suite parameter proposals are NEVER scoped, even though XText might think so... String[] tempReplacementStringParts = getReplacementString().split("\\."); setReplacementString(tempReplacementStringParts[tempReplacementStringParts.length - 1]); } additionalProposalInfoObject = tempAdditionalProposalInfo; super.setAdditionalProposalInfo(tempAdditionalProposalInfo); } @Override public void setProposalContextResource(Resource aContextResource) { proposalContextResource = aContextResource; super.setProposalContextResource(aContextResource); } /** * Returns the additional proposal info object, if possible. This only returns the plain object (but attempts to * resolve it, if necessary). * * @return the object or null */ public EObject getAdditionalProposalInfoObject() { if (resolvedAdditionalProposalInfoObject == null) { EObject tempResult = null; if (additionalProposalInfoObject instanceof EObject) { tempResult = (EObject) additionalProposalInfoObject; } else { if (additionalProposalInfoObject instanceof Provider) { Object tempObject = ((Provider<?>) additionalProposalInfoObject).get(); if (tempObject instanceof EObject) { tempResult = (EObject) tempObject; } } } if (tempResult != null && tempResult.eIsProxy()) { tempResult = EcoreUtil.resolve(tempResult, proposalContextResource); } resolvedAdditionalProposalInfoObject = tempResult; } return resolvedAdditionalProposalInfoObject; } private boolean requiresResolvingForContentAssist(EObject anObject) { return (anObject instanceof TestDefinition || anObject instanceof CallDefinition); } public SuiteDefinition getSuiteDefiningProposedParameter() { return suiteDefiningProposedParameter; } @Override public IInformationControlCreator getInformationControlCreator() { if (useHtmlAdditionalProposalInfo) { return new IInformationControlCreator() { @Override public IInformationControl createInformationControl(Shell aParent) { if (BrowserInformationControl.isAvailable(aParent)) { return new BrowserInformationControl(aParent, JFaceResources.DIALOG_FONT, true); } else { return new DefaultInformationControl(aParent); } } }; } else { // just use the default return super.getInformationControlCreator(); } } /** * Checks whether the proposed element is in the "local" suite. * * @return */ public boolean isReferencingObjectInLocalSuite() { if (getAdditionalProposalInfoObject() != null) { SuiteDefinition tempContainingSuite = IntegrityDSLUtil.findUpstreamContainer(SuiteDefinition.class, (EObject) getAdditionalProposalInfoObject()); if (tempContainingSuite != null) { SuiteDefinition tempCurrentSuite = IntegrityDSLUtil.findUpstreamContainer(SuiteDefinition.class, context.getCurrentModel()); return tempCurrentSuite == tempContainingSuite; } } return false; } /** * Checks whether the proposed element is in the "local" package. * * @return */ public boolean isReferencingObjectInLocalPackage() { if (getAdditionalProposalInfoObject() != null) { PackageDefinition tempContainingPackage = IntegrityDSLUtil.findUpstreamContainer(PackageDefinition.class, (EObject) getAdditionalProposalInfoObject()); if (tempContainingPackage != null) { PackageDefinition tempCurrentPackage = IntegrityDSLUtil.findUpstreamContainer(PackageDefinition.class, context.getCurrentModel()); return tempCurrentPackage == tempContainingPackage; } } return false; } }