| /* |
| * Copyright 2006 Sascha Weinreuter |
| * |
| * 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.intellij.plugins.intelliLang.inject.config; |
| |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.InvalidDataException; |
| import com.intellij.openapi.util.JDOMExternalizableStringList; |
| import com.intellij.openapi.util.JDOMExternalizer; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.patterns.compiler.PatternCompiler; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.PsiFormatUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import gnu.trove.THashMap; |
| import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| public class MethodParameterInjection extends BaseInjection { |
| @NotNull |
| private String myClassName = ""; |
| |
| @NotNull |
| private final Map<String, MethodInfo> myParameterMap = new THashMap<String, MethodInfo>(); |
| |
| public MethodParameterInjection() { |
| super(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID); |
| } |
| |
| @NotNull |
| public String getClassName() { |
| return myClassName; |
| } |
| |
| public void setClassName(@NotNull String className) { |
| myClassName = className; |
| } |
| |
| public void setMethodInfos(final Collection<MethodInfo> newInfos) { |
| myParameterMap.clear(); |
| for (MethodInfo methodInfo : newInfos) { |
| myParameterMap.put(methodInfo.getMethodSignature(), methodInfo); |
| } |
| } |
| |
| public Collection<MethodInfo> getMethodInfos() { |
| return myParameterMap.values(); |
| } |
| |
| public MethodParameterInjection copyFrom(@NotNull BaseInjection o) { |
| super.copyFrom(o); |
| if (o instanceof MethodParameterInjection) { |
| final MethodParameterInjection other = (MethodParameterInjection)o; |
| myClassName = other.getClassName(); |
| myParameterMap.clear(); |
| for (MethodInfo info : other.myParameterMap.values()) { |
| myParameterMap.put(info.methodSignature, info.copy()); |
| } |
| } |
| return this; |
| } |
| |
| protected void readExternalImpl(Element e) { |
| if (e.getAttribute("injector-id") == null) { |
| setClassName(JDOMExternalizer.readString(e, "CLASS")); |
| //setApplyInHierarchy(JDOMExternalizer.readBoolean(e, "APPLY_IN_HIERARCHY")); |
| readOldFormat(e); |
| final THashMap<String, String> map = new THashMap<String, String>(); |
| JDOMExternalizer.readMap(e, map, null, "SIGNATURES"); |
| for (String s : map.keySet()) { |
| final String fixedSignature = fixSignature(s, false); |
| myParameterMap.put(fixedSignature, new MethodInfo(fixedSignature, map.get(s))); |
| } |
| } |
| } |
| |
| private void readOldFormat(final Element e) { |
| final JDOMExternalizableStringList list = new JDOMExternalizableStringList(); |
| try { |
| list.readExternal(e); |
| } |
| catch (InvalidDataException e1) { |
| // nothing |
| } |
| if (list.isEmpty()) return; |
| final boolean[] selection = new boolean[list.size()]; |
| for (int i = 0; i < list.size(); i++) { |
| selection[i] = Boolean.parseBoolean(list.get(i)); |
| } |
| final String methodSignature = fixSignature(JDOMExternalizer.readString(e, "METHOD"), false); |
| myParameterMap.put(methodSignature, new MethodInfo(methodSignature, selection, false)); |
| } |
| |
| |
| @Override |
| public MethodParameterInjection copy() { |
| return new MethodParameterInjection().copyFrom(this); |
| } |
| |
| @Override |
| public void generatePlaces() { |
| final PatternCompiler<PsiElement> compiler = getCompiler(); |
| List<String> patternString = getPatternString(this); |
| InjectionPlace[] places = InjectionPlace.ARRAY_FACTORY.create(patternString.size()); |
| for (int i = 0, patternStringSize = patternString.size(); i < patternStringSize; i++) { |
| String text = patternString.get(i); |
| places[i] = new InjectionPlace(compiler.createElementPattern(text, getDisplayName()), true); |
| } |
| setInjectionPlaces(places); |
| } |
| |
| @SuppressWarnings({"RedundantIfStatement"}) |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| if (!super.equals(o)) return false; |
| |
| final MethodParameterInjection that = (MethodParameterInjection)o; |
| |
| if (!myClassName.equals(that.myClassName)) return false; |
| if (!myParameterMap.equals(that.myParameterMap)) return false; |
| |
| return true; |
| } |
| |
| public int hashCode() { |
| int result = super.hashCode(); |
| result = 31 * result + myClassName.hashCode(); |
| result = 31 * result + myParameterMap.hashCode(); |
| return result; |
| } |
| |
| @NotNull |
| public String getDisplayName() { |
| final String className = getClassName(); |
| if (StringUtil.isEmpty(className)) return "<unnamed>"; |
| MethodInfo singleInfo = null; |
| for (MethodInfo info : myParameterMap.values()) { |
| if (info.isEnabled()) { |
| if (singleInfo == null) { |
| singleInfo = info; |
| } |
| else { |
| singleInfo = null; |
| break; |
| } |
| } |
| } |
| final String name = singleInfo != null |
| ? StringUtil.getShortName(className) + "." + singleInfo.methodName |
| : StringUtil.getShortName(className); |
| return /*"["+getInjectedLanguageId()+"] " +*/ name + " ("+StringUtil.getPackageName(className)+")"; |
| } |
| |
| public static String fixSignature(final String signature, final boolean parameterNames) { |
| @NonNls final StringBuilder sb = new StringBuilder(); |
| final StringTokenizer st = new StringTokenizer(signature, "(,)"); |
| //noinspection ForLoopThatDoesntUseLoopVariable |
| for (int i = 0; st.hasMoreTokens(); i++) { |
| final String token = st.nextToken().trim(); |
| if (i > 1) sb.append(", "); |
| final int idx; |
| if (i == 0) { |
| sb.append(token).append("("); |
| } |
| else if ((idx = token.indexOf(' ')) > -1) { |
| if (parameterNames) { |
| sb.append(token); |
| } |
| else { |
| sb.append(token.substring(0, idx)); |
| } |
| } |
| else { |
| sb.append(token); |
| if (parameterNames) { |
| sb.append(' ').append('p').append(i); |
| } |
| } |
| } |
| sb.append(")"); |
| return sb.toString(); |
| } |
| |
| @NotNull |
| private static String buildSignature(@NotNull PsiMethod method) { |
| return PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, |
| PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.SHOW_FQ_CLASS_NAMES | PsiFormatUtil.SHOW_RAW_TYPE); |
| } |
| |
| public static MethodInfo createMethodInfo(final PsiMethod method) { |
| final String signature = buildSignature(method); |
| return new MethodInfo(signature, new boolean[method.getParameterList().getParametersCount()], false); |
| } |
| |
| public static boolean isInjectable(@Nullable final PsiType type, final Project project) { |
| if (type == null) return false; |
| if (type instanceof PsiPrimitiveType) return false; |
| if (project.isDefault()) { |
| @NonNls final String text = type.getPresentableText(); |
| if (text == null) return false; |
| return text.equals("java.lang.String") || text.equals("java.lang.String...") || text.equals("java.lang.String[]"); |
| } |
| else { |
| return type.equalsToText("java.lang.String") || type.equalsToText("java.lang.String...") || type.equalsToText("java.lang.String[]"); |
| } |
| } |
| |
| @Nullable |
| public static PsiMethod makeMethod(final Project project, final String signature) { |
| if (StringUtil.isEmpty(signature)) return null; |
| try { |
| return JavaPsiFacade.getInstance(project).getElementFactory(). |
| createMethodFromText("void " + fixSignature(signature, true) + "{}", null); |
| } |
| catch (IncorrectOperationException e) { |
| // something wrong |
| } |
| return null; |
| } |
| |
| public static String getParameterTypesString(final String signature) { |
| @NonNls final StringBuilder sb = new StringBuilder(); |
| final StringTokenizer st = new StringTokenizer(signature, "(,)"); |
| //noinspection ForLoopThatDoesntUseLoopVariable |
| for (int i = 0; st.hasMoreTokens(); i++) { |
| final String token = st.nextToken().trim(); |
| if (i > 1) sb.append(", "); |
| final int idx; |
| if (i == 0) { |
| // nothing |
| } |
| else { |
| sb.append('\"'); |
| if ((idx = token.indexOf(' ')) > -1) { |
| sb.append(token.substring(0, idx)); |
| } |
| else { |
| sb.append(token); |
| } |
| sb.append('\"'); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| public static String getPatternStringForJavaPlace(final String methodName, final String parametersStrings, final int parameterIndex, final String className) { |
| final StringBuilder sb = new StringBuilder(); |
| if (parameterIndex >= 0) { |
| sb.append("psiParameter().ofMethod(").append(parameterIndex).append(", "); |
| } |
| sb.append("psiMethod().withName(\"").append(methodName) |
| .append("\").withParameters(").append(parametersStrings) |
| .append(").definedInClass(\"").append(className).append("\")"); |
| if (parameterIndex >= 0) { |
| sb.append(")"); |
| } |
| return sb.toString(); |
| } |
| |
| public static class MethodInfo { |
| @NotNull |
| final String methodSignature; |
| @NotNull |
| final String methodName; |
| @NotNull |
| final boolean[] paramFlags; |
| |
| boolean returnFlag; |
| |
| public MethodInfo(@NotNull final String methodSignature, @NotNull final boolean[] paramFlags, final boolean returnFlag) { |
| this.methodSignature = methodSignature; |
| this.paramFlags = paramFlags; |
| this.returnFlag = returnFlag; |
| methodName = calcMethodName(methodSignature); |
| } |
| |
| public MethodInfo(@NotNull final String methodSignature, @NotNull final String paramFlags) { |
| this.methodSignature = methodSignature; |
| final Pair<boolean[], Boolean> flags = parseFlags(paramFlags); |
| returnFlag = flags.second.booleanValue(); |
| this.paramFlags = flags.first; |
| methodName = calcMethodName(methodSignature); |
| } |
| |
| @NotNull |
| public String getMethodSignature() { |
| return methodSignature; |
| } |
| |
| @NotNull |
| public String getMethodName() { |
| return methodName; |
| } |
| |
| @NotNull |
| public boolean[] getParamFlags() { |
| return paramFlags; |
| } |
| |
| public boolean isReturnFlag() { |
| return returnFlag; |
| } |
| |
| public void setReturnFlag(final boolean returnFlag) { |
| this.returnFlag = returnFlag; |
| } |
| |
| public boolean isEnabled() { |
| if (returnFlag) return true; |
| for (boolean b : paramFlags) { |
| if (b) return true; |
| } |
| return false; |
| } |
| |
| private static Pair<boolean[], Boolean> parseFlags(final String string) { |
| final int returnIdx = string.indexOf(':'); |
| boolean returnFlag = returnIdx != -1 && Boolean.parseBoolean(string.substring(0, returnIdx)); |
| final StringTokenizer st = new StringTokenizer(string.substring(returnIdx+1), ","); |
| final boolean[] result = new boolean[st.countTokens()]; |
| for (int i = 0; i < result.length; i++) { |
| result[i] = Boolean.parseBoolean(st.nextToken()); |
| } |
| return Pair.create(result, returnFlag); |
| } |
| |
| @NonNls |
| private static String calcMethodName(final String methodSignature) { |
| final String s = StringUtil.split(methodSignature, "(").get(0); |
| return s.length() == 0 ? "<none>" : s; |
| } |
| |
| public String getFlagsString() { |
| final StringBuilder result = new StringBuilder(); |
| result.append(returnFlag).append(':'); |
| boolean first = true; |
| for (boolean b : paramFlags) { |
| if (first) first = false; |
| else result.append(','); |
| result.append(b); |
| } |
| return result.toString(); |
| } |
| |
| public MethodInfo copy() { |
| return new MethodInfo(methodSignature, paramFlags.clone(), returnFlag); |
| } |
| |
| public boolean equals(final Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| final MethodInfo that = (MethodInfo)o; |
| |
| if (returnFlag != that.returnFlag) return false; |
| if (!methodName.equals(that.methodName)) return false; |
| if (!methodSignature.equals(that.methodSignature)) return false; |
| if (!Arrays.equals(paramFlags, that.paramFlags)) return false; |
| |
| return true; |
| } |
| |
| public int hashCode() { |
| int result; |
| result = methodSignature.hashCode(); |
| result = 31 * result + methodName.hashCode(); |
| result = 31 * result + Arrays.hashCode(paramFlags); |
| result = 31 * result + (returnFlag ? 1 : 0); |
| return result; |
| } |
| } |
| |
| public static List<String> getPatternString(final MethodParameterInjection injection) { |
| final ArrayList<String> list = new ArrayList<String>(); |
| final String className = injection.getClassName(); |
| for (MethodParameterInjection.MethodInfo info : injection.getMethodInfos()) { |
| final boolean[] paramFlags = info.getParamFlags(); |
| final int paramFlagsLength = paramFlags.length; |
| final String methodName = info.getMethodName(); |
| final String typesString = getParameterTypesString(info.getMethodSignature()); |
| if (info.isReturnFlag()) { |
| list.add(getPatternStringForJavaPlace(methodName, typesString, -1, className)); |
| } |
| for (int i = 0; i < paramFlagsLength; i++) { |
| if (paramFlags[i]) { |
| list.add(getPatternStringForJavaPlace(methodName, typesString, i, className)); |
| } |
| } |
| } |
| return list; |
| } |
| } |
| |