/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.groovy.eclipse.codeassist.completions; import java.lang.reflect.Method; import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist; import org.codehaus.groovy.eclipse.codeassist.ProposalUtils; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.groovy.core.util.ReflectionUtils; import org.eclipse.jdt.groovy.search.VariableScope; import org.eclipse.jdt.internal.ui.text.java.ParameterGuesser; import org.eclipse.jdt.internal.ui.text.template.contentassist.PositionBasedCompletionProposal; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.contentassist.ICompletionProposal; /** * A delegate to {@link ParameterGuesser}. Used because e3.6 and e3.7+ * use different variants of {@link ParameterGuesser}. This class wraps calls * to the other class and does some extra work to make the parameter guesses * slightly more groovy. */ public class ParameterGuesserDelegate { private static final String CLOSURE_TEXT = "{ }"; private static final String EMPTY_STRING = "\"\""; private static final String NULL_TEXT = "null"; private ParameterGuesser guesser; public ParameterGuesserDelegate(IJavaElement enclosingElement) { guesser = new ParameterGuesser(enclosingElement); } // unfortunately, the parameterProposals method has a different signature in // 3.6 and 3.7. // so must call using reflection public ICompletionProposal[] parameterProposals(String parameterType, String paramName, Position position, IJavaElement[] assignable, boolean fillBestGuess) { parameterType = convertToPrimitive(parameterType); Method method = findParameterProposalsMethod(); try { ICompletionProposal[] allCompletions; if (method.getParameterTypes().length == 5) { // 3.6 allCompletions = (ICompletionProposal[]) method.invoke(guesser, parameterType, paramName, position, assignable, fillBestGuess); } else { // 3.7 and later allCompletions = (ICompletionProposal[]) method.invoke(guesser, parameterType, paramName, position, assignable, fillBestGuess, false); } // ensure enum proposals insert the declaring type as part of the // name. if (allCompletions != null && allCompletions.length > 0 && assignable != null && assignable.length > 0) { IType declaring = (IType) assignable[0].getAncestor(IJavaElement.TYPE); if (declaring != null && declaring.isEnum()) { // each enum is proposed twice. The first time, use the // qualified name and the second keep with the simple name boolean useFull = true; for (int i = 0; i < assignable.length && i < allCompletions.length; i++) { if (assignable[i].getElementType() == IJavaElement.FIELD) { if (useFull) { String newReplacement = declaring.getElementName() + '.' + assignable[i].getElementName(); ReflectionUtils.setPrivateField(PositionBasedCompletionProposal.class, "fReplacementString", allCompletions[i], newReplacement); ReflectionUtils.setPrivateField(PositionBasedCompletionProposal.class, "fDisplayString", allCompletions[i], newReplacement); useFull = false; } else { useFull = true; } } } } } return addExtras(allCompletions, parameterType, position); } catch (Exception e) { GroovyContentAssist.logError("Exception trying to reflectively invoke 'parameterProposals' method.", e); return ProposalUtils.NO_COMPLETIONS; } } private String convertToPrimitive(String parameterType) { if ("java.lang.Short".equals(parameterType)) { //$NON-NLS-1$ return "short"; } if ("java.lang.Integer".equals(parameterType)) { //$NON-NLS-1$ return "int"; } if ("java.lang.Long".equals(parameterType)) { //$NON-NLS-1$ return "long"; } if ("java.lang.Float".equals(parameterType)) { //$NON-NLS-1$ return "float"; } if ("java.lang.Double".equals(parameterType)) { //$NON-NLS-1$ return "double"; } if ("java.lang.Character".equals(parameterType)) { //$NON-NLS-1$ return "char"; } if ("java.lang.Byte".equals(parameterType)) { //$NON-NLS-1$ return "byte"; } if ("java.lang.Boolean".equals(parameterType)) { //$NON-NLS-1$ return "boolean"; } return parameterType; } private static Method parameterProposalsMethod; private static Method findParameterProposalsMethod() { if (parameterProposalsMethod == null) { try { // 3.6 parameterProposalsMethod = ParameterGuesser.class.getMethod("parameterProposals", String.class, String.class, Position.class, IJavaElement[].class, boolean.class); } catch (SecurityException e) { GroovyContentAssist.logError("Exception trying to reflectively find 'parameterProposals' method.", e); } catch (NoSuchMethodException e) { // 3.7 RC4 or later try { parameterProposalsMethod = ParameterGuesser.class.getMethod("parameterProposals", String.class, String.class, Position.class, IJavaElement[].class, boolean.class, boolean.class); } catch (SecurityException e1) { GroovyContentAssist.logError("Exception trying to reflectively find 'parameterProposals' method.", e1); } catch (NoSuchMethodException e1) { GroovyContentAssist.logError("Exception trying to reflectively find 'parameterProposals' method.", e1); } } } return parameterProposalsMethod; } /** * Adds extra parameter proposals that are groovy specific, eg- Empty strings * and empty closures. */ private ICompletionProposal[] addExtras(ICompletionProposal[] parameterProposals, String expectedType, Position position) { ICompletionProposal proposal = null; if (expectedType.equals(VariableScope.STRING_CLASS_NODE.getName())) { proposal = new PositionBasedCompletionProposal(EMPTY_STRING, position, 1); } else if (expectedType.equals(VariableScope.CLOSURE_CLASS_NODE.getName())) { proposal = new PositionBasedCompletionProposal(CLOSURE_TEXT, position, 2); } if (proposal != null) { int origLen = parameterProposals.length; // make the extra proposal come before 'null' if (parameterProposals[origLen - 1].getDisplayString().equals(NULL_TEXT)) { parameterProposals[origLen - 1] = proposal; } else { ICompletionProposal[] newProps = new ICompletionProposal[origLen + 1]; System.arraycopy(parameterProposals, 0, newProps, 0, origLen); parameterProposals = newProps; parameterProposals[origLen] = proposal; } } return parameterProposals; } }