diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java index b4c33e9bb69..3240652c998 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java @@ -33,6 +33,8 @@ import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; +import org.jetbrains.jet.lang.resolve.java.extAnnotations.CoreAnnotationsProvider; +import org.jetbrains.jet.lang.resolve.java.extAnnotations.ExternalAnnotationsProvider; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.jet.plugin.JetFileType; @@ -91,6 +93,9 @@ public class JetCoreEnvironment extends JavaCoreEnvironment { addLibraryRoot(root); } } + + ExternalAnnotationsProvider.setInstance(new CoreAnnotationsProvider()); + if (compilerSpecialMode.includeKotlinRuntime()) { addToClasspath(compilerDependencies.getRuntimeJar()); } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/ClassUtil.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/ClassUtil.java new file mode 100644 index 00000000000..d713912316c --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/ClassUtil.java @@ -0,0 +1,203 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * 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.jetbrains.jet.lang.resolve.java.extAnnotations; + +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.StringBuilderSpinAllocator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This class is copied from IDEA. + * This class should be eliminated when Kotlin will depend on IDEA 12.x (KT-2326) + * @author Evgeny Gerashchenko + * @since 6/26/12 + */ +@Deprecated +public class ClassUtil { + private ClassUtil() {} + + public static void formatClassName(@NotNull final PsiClass aClass, final StringBuilder buf) { + final String qName = aClass.getQualifiedName(); + if (qName != null) { + buf.append(qName); + } + else { + final PsiClass parentClass = getContainerClass(aClass); + if (parentClass != null) { + formatClassName(parentClass, buf); + buf.append("$"); + buf.append(getNonQualifiedClassIdx(aClass)); + final String name = aClass.getName(); + if (name != null) { + buf.append(name); + } + } + } + } + + @Nullable + private static PsiClass getContainerClass(final PsiClass aClass) { + PsiElement parent = aClass.getContext(); + while (parent != null && !(parent instanceof PsiClass)) { + parent = parent.getContext(); + } + return (PsiClass)parent; + } + + public static int getNonQualifiedClassIdx(@NotNull final PsiClass psiClass) { + final int[] result = {-1}; + final PsiClass containingClass = getContainerClass(psiClass); + if (containingClass != null) { + containingClass.accept(new JavaRecursiveElementVisitor() { + private int myCurrentIdx = 0; + + @Override public void visitElement(PsiElement element) { + if (result[0] == -1) { + super.visitElement(element); + } + } + + @Override public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (aClass.getQualifiedName() == null) { + myCurrentIdx++; + if (psiClass == aClass) { + result[0] = myCurrentIdx; + } + } + } + }); + } + return result[0]; + } + + public static PsiClass findNonQualifiedClassByIndex(final String indexName, @NotNull final PsiClass containingClass, + final boolean jvmCompatible) { + String prefix = getDigitPrefix(indexName); + final int idx = prefix.length() > 0 ? Integer.parseInt(prefix) : -1; + final String name = prefix.length() < indexName.length() ? indexName.substring(prefix.length()) : null; + final PsiClass[] result = new PsiClass[1]; + containingClass.accept(new JavaRecursiveElementVisitor() { + private int myCurrentIdx = 0; + + @Override public void visitElement(PsiElement element) { + if (result[0] == null) { + super.visitElement(element); + } + } + + @Override public void visitClass(PsiClass aClass) { + if (!jvmCompatible) { + super.visitClass(aClass); + if (aClass.getQualifiedName() == null) { + myCurrentIdx++; + if (myCurrentIdx == idx && Comparing.strEqual(name, aClass.getName())) { + result[0] = aClass; + } + } + return; + } + if (aClass == containingClass) { + super.visitClass(aClass); + return; + } + if (Comparing.strEqual(name, aClass.getName())) { + myCurrentIdx++; + if (myCurrentIdx == idx || idx == -1) { + result[0] = aClass; + } + } + } + + @Override public void visitTypeParameter(final PsiTypeParameter classParameter) { + if (!jvmCompatible) { + super.visitTypeParameter(classParameter); + } + else { + visitElement(classParameter); + } + } + }); + return result[0]; + } + + private static String getDigitPrefix(final String indexName) { + final StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + for (int i = 0; i < indexName.length(); i++) { + final char c = indexName.charAt(i); + if (Character.isDigit(c)) { + builder.append(c); + } + else { + break; + } + } + return builder.toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + + + @Nullable + public static PsiClass findPsiClass(final PsiManager psiManager, + String externalName, + PsiClass psiClass, + boolean jvmCompatible) { + return findPsiClass(psiManager, externalName, psiClass, jvmCompatible, GlobalSearchScope.allScope(psiManager.getProject())); + } + + @Nullable + public static PsiClass findPsiClass(final PsiManager psiManager, + String externalName, + @Nullable PsiClass psiClass, + boolean jvmCompatible, + final GlobalSearchScope scope) { + final int topIdx = externalName.indexOf('$'); + if (topIdx > -1) { + if (psiClass == null) { + psiClass = JavaPsiFacade.getInstance(psiManager.getProject()) + .findClass(externalName.substring(0, topIdx), scope); + } + if (psiClass == null) return null; + externalName = externalName.substring(topIdx + 1); + return findSubclass(psiManager, externalName, psiClass, jvmCompatible); + } else { + return JavaPsiFacade.getInstance(psiManager.getProject()).findClass(externalName, scope); + } + } + + @Nullable + private static PsiClass findSubclass(final PsiManager psiManager, + final String externalName, + final PsiClass psiClass, + final boolean jvmCompatible) { + final int nextIdx = externalName.indexOf('$'); + if (nextIdx > -1) { + final PsiClass anonymousClass = findNonQualifiedClassByIndex(externalName.substring(0, nextIdx), psiClass, jvmCompatible); + if (anonymousClass == null) return null; + return findPsiClass(psiManager, externalName.substring(nextIdx), anonymousClass, jvmCompatible); + } + else { + return findNonQualifiedClassByIndex(externalName, psiClass, jvmCompatible); + } + } +} \ No newline at end of file diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/CoreAnnotationsProvider.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/CoreAnnotationsProvider.java new file mode 100644 index 00000000000..721bf6ce7a7 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/CoreAnnotationsProvider.java @@ -0,0 +1,246 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * 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.jetbrains.jet.lang.resolve.java.extAnnotations; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.StringBuilderSpinAllocator; +import com.intellij.util.containers.ConcurrentWeakHashMap; +import com.intellij.util.containers.ConcurrentWeakValueHashMap; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentMap; + +/** + * This class is copied from IDEA. + * This class should be eliminated when Kotlin will depend on IDEA 12.x (KT-2326) + * @author Evgeny Gerashchenko + * @since 6/26/12 + */ +@Deprecated +public class CoreAnnotationsProvider extends ExternalAnnotationsProvider { + private static final Logger LOG = Logger.getInstance("#" + CoreAnnotationsProvider.class.getName()); + @NotNull private static final List NULL = new ArrayList(); + @NotNull private final ConcurrentMap> + myExternalAnnotations = new ConcurrentWeakValueHashMap>(); + + private List externalAnnotationsRoots = new ArrayList(); + + public CoreAnnotationsProvider() { + } + + @Nullable + private static String getExternalName(PsiModifierListOwner listOwner, boolean showParamName) { + return PsiFormatUtil.getExternalName(listOwner, showParamName, Integer.MAX_VALUE); + } + + @Nullable + private static String getFQN(String packageName, @Nullable VirtualFile virtualFile) { + if (virtualFile == null) return null; + return StringUtil.getQualifiedName(packageName, virtualFile.getNameWithoutExtension()); + } + + @Nullable + protected static String getNormalizedExternalName(@NotNull PsiModifierListOwner owner) { + String externalName = getExternalName(owner, true); + if (externalName != null) { + if (owner instanceof PsiParameter && owner.getParent() instanceof PsiParameterList) { + final PsiMethod method = PsiTreeUtil.getParentOfType(owner, PsiMethod.class); + if (method != null) { + externalName = + externalName.substring(0, externalName.lastIndexOf(' ') + 1) + method.getParameterList().getParameterIndex((PsiParameter)owner); + } + } + final int idx = externalName.indexOf('('); + if (idx == -1) return externalName; + final StringBuilder buf = StringBuilderSpinAllocator.alloc(); + try { + final int rightIdx = externalName.indexOf(')'); + final String[] params = externalName.substring(idx + 1, rightIdx).split(","); + buf.append(externalName.substring(0, idx + 1)); + for (String param : params) { + param = param.trim(); + final int spaceIdx = param.indexOf(' '); + buf.append(spaceIdx > -1 ? param.substring(0, spaceIdx) : param).append(", "); + } + return StringUtil.trimEnd(buf.toString(), ", ") + externalName.substring(rightIdx); + } + finally { + StringBuilderSpinAllocator.dispose(buf); + } + } + return externalName; + } + + @Override + @Nullable + public PsiAnnotation findExternalAnnotation(@NotNull Project project, @NotNull final PsiModifierListOwner listOwner, @NotNull final String annotationFQN) { + return collectExternalAnnotations(listOwner).get(annotationFQN); + } + + @Override + @Nullable + public PsiAnnotation[] findExternalAnnotations(@NotNull Project project, @NotNull final PsiModifierListOwner listOwner) { + final Map result = collectExternalAnnotations(listOwner); + return result.isEmpty() ? null : result.values().toArray(new PsiAnnotation[result.size()]); + } + + private final Map> cache = new ConcurrentWeakHashMap>(); + @NotNull + private Map collectExternalAnnotations(@NotNull final PsiModifierListOwner listOwner) { + Map map = cache.get(listOwner); + if (map == null) { + map = doCollect(listOwner); + cache.put(listOwner, map); + } + return map; + } + + public void addExternalAnnotationsRoot(VirtualFile externalAnnotationsRoot) { + externalAnnotationsRoots.add(externalAnnotationsRoot); + } + + private Map doCollect(@NotNull PsiModifierListOwner listOwner) { + final List files = findExternalAnnotationsFiles(listOwner); + if (files == null) { + return Collections.emptyMap(); + } + final Map result = new HashMap(); + for (PsiFile file : files) { + if (!file.isValid()) continue; + final Document document; + try { + document = JDOMUtil.loadDocument(escapeAttributes(StreamUtil.readText(file.getVirtualFile().getInputStream()))); + } + catch (IOException e) { + + LOG.error(e); + continue; + } + catch (JDOMException e) { + LOG.error(e); + continue; + } + if (document == null) continue; + final Element rootElement = document.getRootElement(); + if (rootElement == null) continue; + final String externalName = getExternalName(listOwner, false); + final String oldExternalName = getNormalizedExternalName(listOwner); + //noinspection unchecked + for (final Element element : (List) rootElement.getChildren()) { + final String className = element.getAttributeValue("name"); + if (!Comparing.strEqual(className, externalName) && !Comparing.strEqual(className, oldExternalName)) { + continue; + } + //noinspection unchecked + for (Element annotationElement : (List) element.getChildren()) { + final String annotationFQN = annotationElement.getAttributeValue("name"); + final StringBuilder buf = new StringBuilder(); + //noinspection unchecked + for (Element annotationParameter : (List) annotationElement.getChildren()) { + buf.append(","); + final String nameValue = annotationParameter.getAttributeValue("name"); + if (nameValue != null) { + buf.append(nameValue).append("="); + } + buf.append(annotationParameter.getAttributeValue("val")); + } + final String annotationText = + "@" + annotationFQN + (buf.length() > 0 ? "(" + StringUtil.trimStart(buf.toString(), ",") + ")" : ""); + try { + result.put(annotationFQN, + JavaPsiFacade.getInstance(listOwner.getProject()).getElementFactory().createAnnotationFromText( + annotationText, null)); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + } + return result; + } + + @Nullable + private List findExternalAnnotationsFiles(@NotNull PsiModifierListOwner listOwner) { + final PsiFile containingFile = listOwner.getContainingFile(); + if (!(containingFile instanceof PsiJavaFile)) { + return null; + } + final PsiJavaFile javaFile = (PsiJavaFile)containingFile; + final String packageName = javaFile.getPackageName(); + final VirtualFile virtualFile = containingFile.getVirtualFile(); + String fqn = getFQN(packageName, virtualFile); + if (fqn == null) return null; + final List files = myExternalAnnotations.get(fqn); + if (files == NULL) return null; + if (files != null) { + for (Iterator it = files.iterator(); it.hasNext();) { + if (!it.next().isValid()) it.remove(); + } + return files; + } + + if (virtualFile == null) { + return null; + } + + List possibleAnnotationsXmls = new ArrayList(); + for (VirtualFile root : externalAnnotationsRoots) { + final VirtualFile ext = root.findFileByRelativePath(packageName.replace(".", "/") + "/" + "annotations.xml"); + if (ext == null) continue; + final PsiFile psiFile = listOwner.getManager().findFile(ext); + possibleAnnotationsXmls.add(psiFile); + } + if (!possibleAnnotationsXmls.isEmpty()) { + myExternalAnnotations.put(fqn, possibleAnnotationsXmls); + return possibleAnnotationsXmls; + } + myExternalAnnotations.put(fqn, NULL); + return null; + } + + + // This method is used for legacy reasons. + // Old external annotations sometimes are bad XML: they have "<" and ">" characters in attributes values. To prevent SAX parser from + // failing, we escape attributes values. + @NotNull + private static String escapeAttributes(@NotNull String invalidXml) { + // We assume that XML has single- and double-quote characters only for attribute values, therefore we don't any complex parsing, + // just split by ['"] regexp instead + String[] split = invalidXml.split("[\"\']"); + assert split.length % 2 == 1; + for (int i = 1; i < split.length; i += 2) { + split[i] = split[i].replace("<", "<").replace(">", ">"); + } + return StringUtil.join(split, "\""); + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiExpressionTrimRenderer.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiExpressionTrimRenderer.java new file mode 100644 index 00000000000..2e1da0eae33 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiExpressionTrimRenderer.java @@ -0,0 +1,235 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * 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. + */ + +/* + * User: anna + * Date: 28-Oct-2008 + */ +package org.jetbrains.jet.lang.resolve.java.extAnnotations; + +import com.intellij.psi.*; +import com.intellij.util.Function; + +/** + * This class is copied from IDEA. + * This class should be eliminated when Kotlin will depend on IDEA 12.x (KT-2326) + * @author Evgeny Gerashchenko + * @since 6/26/12 + */ +@Deprecated +public class PsiExpressionTrimRenderer extends JavaRecursiveElementWalkingVisitor { + private final StringBuilder myBuf; + + public PsiExpressionTrimRenderer(final StringBuilder buf) { + myBuf = buf; + } + + @Override + public void visitExpression(final PsiExpression expression) { + myBuf.append(expression.getText()); + } + + @Override + public void visitInstanceOfExpression(final PsiInstanceOfExpression expression) { + expression.getOperand().accept(this); + myBuf.append(" ").append(PsiKeyword.INSTANCEOF).append(" "); + final PsiTypeElement checkType = expression.getCheckType(); + if (checkType != null) { + myBuf.append(checkType.getText()); + } + } + + @Override + public void visitParenthesizedExpression(final PsiParenthesizedExpression expression) { + myBuf.append("("); + final PsiExpression expr = expression.getExpression(); + if (expr != null) { + expr.accept(this); + } + myBuf.append(")"); + } + + @Override + public void visitTypeCastExpression(final PsiTypeCastExpression expression) { + final PsiTypeElement castType = expression.getCastType(); + if (castType != null) { + myBuf.append("(").append(castType.getText()).append(")"); + } + final PsiExpression operand = expression.getOperand(); + if (operand != null) { + operand.accept(this); + } + } + + @Override + public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) { + expression.getArrayExpression().accept(this); + myBuf.append("["); + final PsiExpression indexExpression = expression.getIndexExpression(); + if (indexExpression != null) { + indexExpression.accept(this); + } + myBuf.append("]"); + } + + @Override + public void visitPrefixExpression(final PsiPrefixExpression expression) { + myBuf.append(expression.getOperationSign().getText()); + final PsiExpression operand = expression.getOperand(); + if (operand != null) { + operand.accept(this); + } + } + + @Override + public void visitPostfixExpression(final PsiPostfixExpression expression) { + expression.getOperand().accept(this); + myBuf.append(expression.getOperationSign().getText()); + } + + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + PsiExpression[] operands = expression.getOperands(); + for (int i = 0; i < operands.length; i++) { + PsiExpression operand = operands[i]; + if (i != 0) { + PsiJavaToken token = expression.getTokenBeforeOperand(operand); + myBuf.append(" ").append(token.getText()).append(" "); + } + operand.accept(this); + } + } + + @Override + public void visitConditionalExpression(final PsiConditionalExpression expression) { + expression.getCondition().accept(this); + + myBuf.append(" ? "); + final PsiExpression thenExpression = expression.getThenExpression(); + if (thenExpression != null) { + thenExpression.accept(this); + } + + myBuf.append(" : "); + final PsiExpression elseExpression = expression.getElseExpression(); + if (elseExpression != null) { + elseExpression.accept(this); + } + } + + @Override + public void visitAssignmentExpression(final PsiAssignmentExpression expression) { + expression.getLExpression().accept(this); + myBuf.append(expression.getOperationSign().getText()); + final PsiExpression rExpression = expression.getRExpression(); + if (rExpression != null) { + rExpression.accept(this); + } + } + + @Override + public void visitReferenceExpression(final PsiReferenceExpression expr) { + final PsiExpression qualifierExpression = expr.getQualifierExpression(); + if (qualifierExpression != null) { + qualifierExpression.accept(this); + myBuf.append("."); + } + myBuf.append(expr.getReferenceName()); + + } + + @Override + public void visitMethodCallExpression(final PsiMethodCallExpression expr) { + expr.getMethodExpression().accept(this); + expr.getArgumentList().accept(this); + } + + + @Override + public void visitArrayInitializerExpression(final PsiArrayInitializerExpression expression) { + myBuf.append("{"); + boolean first = true; + for (PsiExpression expr : expression.getInitializers()) { + if (!first) { + myBuf.append(", "); + } + first = false; + expr.accept(this); + } + myBuf.append("}"); + } + + @Override + public void visitExpressionList(final PsiExpressionList list) { + final PsiExpression[] args = list.getExpressions(); + if (args.length > 0) { + myBuf.append("(...)"); + } + else { + myBuf.append("()"); + } + } + + @Override + public void visitNewExpression(final PsiNewExpression expr) { + final PsiAnonymousClass anonymousClass = expr.getAnonymousClass(); + + final PsiExpressionList argumentList = expr.getArgumentList(); + + if (anonymousClass != null) { + myBuf.append(PsiKeyword.NEW).append(" ").append(anonymousClass.getBaseClassType().getPresentableText()); + if (argumentList != null) argumentList.accept(this); + myBuf.append(" {...}"); + } + else { + final PsiJavaCodeReferenceElement reference = expr.getClassReference(); + if (reference != null) { + myBuf.append(PsiKeyword.NEW).append(" ").append(reference.getText()); + + final PsiExpression[] arrayDimensions = expr.getArrayDimensions(); + final PsiType type = expr.getType(); + final int dimensions = type != null ? type.getArrayDimensions() : arrayDimensions.length; + if (arrayDimensions.length > 0) myBuf.append("["); + for (int i = 0, arrayDimensionsLength = arrayDimensions.length; i < dimensions; i++) { + final PsiExpression dimension = i < arrayDimensionsLength ? arrayDimensions[i] : null; + if (i > 0) myBuf.append("]["); + if (dimension != null) { + dimension.accept(this); + } + } + if (arrayDimensions.length > 0) myBuf.append("]"); + + if (argumentList != null) { + argumentList.accept(this); + } + + final PsiArrayInitializerExpression arrayInitializer = expr.getArrayInitializer(); + if (arrayInitializer != null) { + arrayInitializer.accept(this); + } + } + else { + myBuf.append(expr.getText()); + } + } + } + + public static String render(PsiExpression expression) { + StringBuilder buf = new StringBuilder(); + expression.accept(new PsiExpressionTrimRenderer(buf)); + return buf.toString(); + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiFormatUtil.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiFormatUtil.java new file mode 100644 index 00000000000..3a14a2bda70 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiFormatUtil.java @@ -0,0 +1,360 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * 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.jetbrains.jet.lang.resolve.java.extAnnotations; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This class is copied from IDEA. + * This class should be eliminated when Kotlin will depend on IDEA 12.x (KT-2326) + * @author Evgeny Gerashchenko + * @since 6/26/12 + */ +@Deprecated +public class PsiFormatUtil extends PsiFormatUtilBase { + @MagicConstant(flags = {SHOW_MODIFIERS, SHOW_TYPE, TYPE_AFTER, SHOW_CONTAINING_CLASS, SHOW_FQ_NAME, SHOW_NAME, SHOW_MODIFIERS, SHOW_INITIALIZER, SHOW_RAW_TYPE, SHOW_RAW_NON_TOP_TYPE, SHOW_FQ_CLASS_NAMES}) + public @interface FormatVariableOptions {} + + public static String formatVariable(PsiVariable variable, @FormatVariableOptions int options, PsiSubstitutor substitutor){ + StringBuilder buffer = new StringBuilder(); + formatVariable(variable, options, substitutor,buffer); + return buffer.toString(); + } + private static void formatVariable(PsiVariable variable, + @FormatVariableOptions int options, + PsiSubstitutor substitutor, + @NotNull StringBuilder buffer){ + if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) == 0){ + formatModifiers(variable, options,buffer); + } + if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) == 0){ + appendSpaceIfNeeded(buffer); + buffer.append(formatType(variable.getType(), options, substitutor)); + } + if (variable instanceof PsiField && (options & SHOW_CONTAINING_CLASS) != 0){ + PsiClass aClass = ((PsiField)variable).getContainingClass(); + if (aClass != null){ + String className = aClass.getName(); + if (className != null) { + appendSpaceIfNeeded(buffer); + if ((options & SHOW_FQ_NAME) != 0){ + String qName = aClass.getQualifiedName(); + if (qName != null){ + buffer.append(qName); + } + else{ + buffer.append(className); + } + } + else{ + buffer.append(className); + } + buffer.append('.'); + } + } + if ((options & SHOW_NAME) != 0){ + buffer.append(variable.getName()); + } + } + else{ + if ((options & SHOW_NAME) != 0){ + String name = variable.getName(); + if (StringUtil.isNotEmpty(name)){ + appendSpaceIfNeeded(buffer); + buffer.append(name); + } + } + } + if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) != 0){ + if ((options & SHOW_NAME) != 0 && variable.getName() != null){ + buffer.append(':'); + } + buffer.append(formatType(variable.getType(), options, substitutor)); + } + if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) != 0){ + formatModifiers(variable, options,buffer); + } + if ((options & SHOW_INITIALIZER) != 0){ + PsiExpression initializer = variable.getInitializer(); + if (initializer != null){ + buffer.append(" = "); + String text = PsiExpressionTrimRenderer.render(initializer); + int index1 = text.lastIndexOf('\n'); + if (index1 < 0) index1 = text.length(); + int index2 = text.lastIndexOf('\r'); + if (index2 < 0) index2 = text.length(); + int index = Math.min(index1, index2); + buffer.append(text.substring(0, index)); + if (index < text.length()) { + buffer.append(" ..."); + } + } + } + } + + @MagicConstant(flags = {SHOW_MODIFIERS, MODIFIERS_AFTER, SHOW_TYPE, TYPE_AFTER, SHOW_CONTAINING_CLASS, SHOW_FQ_NAME, SHOW_NAME, SHOW_PARAMETERS, SHOW_THROWS, SHOW_RAW_TYPE, SHOW_RAW_NON_TOP_TYPE, SHOW_FQ_CLASS_NAMES}) + public @interface FormatMethodOptions {} + + private static void formatMethod(PsiMethod method, PsiSubstitutor substitutor, @FormatMethodOptions int options, @FormatVariableOptions int parameterOptions, int maxParametersToShow, StringBuilder buffer){ + if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) == 0){ + formatModifiers(method, options,buffer); + } + if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) == 0){ + PsiType type = method.getReturnType(); + if (type != null){ + appendSpaceIfNeeded(buffer); + buffer.append(formatType(type, options, substitutor)); + } + } + if ((options & SHOW_CONTAINING_CLASS) != 0){ + PsiClass aClass = method.getContainingClass(); + if (aClass != null){ + appendSpaceIfNeeded(buffer); + String name = aClass.getName(); + if (name != null) { + if ((options & SHOW_FQ_NAME) != 0){ + String qName = aClass.getQualifiedName(); + if (qName != null){ + buffer.append(qName); + } + else{ + buffer.append(name); + } + } + else{ + buffer.append(name); + } + buffer.append('.'); + } + } + if ((options & SHOW_NAME) != 0){ + buffer.append(method.getName()); + } + } + else{ + if ((options & SHOW_NAME) != 0){ + appendSpaceIfNeeded(buffer); + buffer.append(method.getName()); + } + } + if ((options & SHOW_PARAMETERS) != 0){ + buffer.append('('); + PsiParameter[] parms = method.getParameterList().getParameters(); + for(int i = 0; i < Math.min(parms.length, maxParametersToShow); i++) { + PsiParameter parm = parms[i]; + if (i > 0){ + buffer.append(", "); + } + buffer.append(formatVariable(parm, parameterOptions, substitutor)); + } + if(parms.length > maxParametersToShow) { + buffer.append (", ..."); + } + buffer.append(')'); + } + if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) != 0){ + PsiType type = method.getReturnType(); + if (type != null){ + if (buffer.length() > 0){ + buffer.append(':'); + } + buffer.append(formatType(type, options, substitutor)); + } + } + if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) != 0){ + formatModifiers(method, options,buffer); + } + if ((options & SHOW_THROWS) != 0){ + String throwsText = formatReferenceList(method.getThrowsList(), options); + if (!throwsText.isEmpty()){ + appendSpaceIfNeeded(buffer); + //noinspection HardCodedStringLiteral + buffer.append("throws "); + buffer.append(throwsText); + } + } + } + + + private static void formatModifiers(PsiElement element, int options, StringBuilder buffer) throws IllegalArgumentException{ + PsiModifierList list; + boolean isInterface = false; + if (element instanceof PsiVariable){ + list = ((PsiVariable)element).getModifierList(); + } + else if (element instanceof PsiMethod){ + list = ((PsiMethod)element).getModifierList(); + } + else if (element instanceof PsiClass){ + isInterface = ((PsiClass)element).isInterface(); + list = ((PsiClass)element).getModifierList(); + if (list == null) return; + } + else if (element instanceof PsiClassInitializer){ + list = ((PsiClassInitializer)element).getModifierList(); + if (list == null) return; + } + else{ + throw new IllegalArgumentException(); + } + if (list == null) return; + if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 + ? list.hasExplicitModifier(PsiModifier.PUBLIC) + : list.hasModifierProperty(PsiModifier.PUBLIC)) { + appendModifier(buffer, PsiModifier.PUBLIC); + } + + if (list.hasModifierProperty(PsiModifier.PROTECTED)){ + appendModifier(buffer, PsiModifier.PROTECTED); + } + if (list.hasModifierProperty(PsiModifier.PRIVATE)){ + appendModifier(buffer, PsiModifier.PRIVATE); + } + + if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 + ? list.hasExplicitModifier(PsiModifier.PACKAGE_LOCAL) + : list.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { + if (element instanceof PsiClass && element.getParent() instanceof PsiDeclarationStatement) {// local class + appendModifier(buffer, "local"); + } + else { + appendModifier(buffer, PsiBundle.visibilityPresentation(PsiModifier.PACKAGE_LOCAL)); + } + } + + if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 + ? list.hasExplicitModifier(PsiModifier.STATIC) + : list.hasModifierProperty(PsiModifier.STATIC)) appendModifier(buffer, PsiModifier.STATIC); + + if (!isInterface && //cls modifier list + ((options & SHOW_REDUNDANT_MODIFIERS) == 0 + ? list.hasExplicitModifier(PsiModifier.ABSTRACT) + : list.hasModifierProperty(PsiModifier.ABSTRACT))) appendModifier(buffer, PsiModifier.ABSTRACT); + + if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 + ? list.hasExplicitModifier(PsiModifier.FINAL) + : list.hasModifierProperty(PsiModifier.FINAL)) appendModifier(buffer, PsiModifier.FINAL); + + if (list.hasModifierProperty(PsiModifier.NATIVE) && (options & JAVADOC_MODIFIERS_ONLY) == 0){ + appendModifier(buffer, PsiModifier.NATIVE); + } + if (list.hasModifierProperty(PsiModifier.SYNCHRONIZED) && (options & JAVADOC_MODIFIERS_ONLY) == 0){ + appendModifier(buffer, PsiModifier.SYNCHRONIZED); + } + if (list.hasModifierProperty(PsiModifier.STRICTFP) && (options & JAVADOC_MODIFIERS_ONLY) == 0){ + appendModifier(buffer, PsiModifier.STRICTFP); + } + if (list.hasModifierProperty(PsiModifier.TRANSIENT) && + element instanceof PsiVariable // javac 5 puts transient attr for methods + ){ + appendModifier(buffer, PsiModifier.TRANSIENT); + } + if (list.hasModifierProperty(PsiModifier.VOLATILE)){ + appendModifier(buffer, PsiModifier.VOLATILE); + } + } + + private static void appendModifier(final StringBuilder buffer, final String modifier) { + appendSpaceIfNeeded(buffer); + buffer.append(modifier); + } + + public static String formatReferenceList(PsiReferenceList list, int options){ + StringBuilder buffer = new StringBuilder(); + PsiJavaCodeReferenceElement[] refs = list.getReferenceElements(); + for(int i = 0; i < refs.length; i++) { + PsiJavaCodeReferenceElement ref = refs[i]; + if (i > 0){ + buffer.append(", "); + } + buffer.append(formatReference(ref, options)); + } + return buffer.toString(); + } + + public static String formatType(PsiType type, int options, @NotNull PsiSubstitutor substitutor){ + type = substitutor.substitute(type); + if ((options & SHOW_RAW_TYPE) != 0) { + type = TypeConversionUtil.erasure(type); + } else if ((options & SHOW_RAW_NON_TOP_TYPE) != 0) { + if (!(PsiUtil.resolveClassInType(type) instanceof PsiTypeParameter)) { + final boolean preserveEllipsis = type instanceof PsiEllipsisType; + type = TypeConversionUtil.erasure(type); + if (preserveEllipsis && type instanceof PsiArrayType) { + type = new PsiEllipsisType(((PsiArrayType)type).getComponentType()); + } + } + } + return (options & SHOW_FQ_CLASS_NAMES) == 0 ? type.getPresentableText() : type.getInternalCanonicalText(); + } + + public static String formatReference(PsiJavaCodeReferenceElement ref, int options){ + return (options & SHOW_FQ_CLASS_NAMES) == 0 ? ref.getText() : ref.getCanonicalText(); + } + + @Nullable + public static String getExternalName(PsiModifierListOwner owner, final boolean showParamName, int maxParamsToShow) { + final StringBuilder builder = new StringBuilder(); + if (owner instanceof PsiClass) { + ClassUtil.formatClassName((PsiClass)owner, builder); + return builder.toString(); + } + final PsiClass psiClass = PsiTreeUtil.getParentOfType(owner, PsiClass.class, false); + if (psiClass == null) return null; + ClassUtil.formatClassName(psiClass, builder); + if (owner instanceof PsiMethod) { + builder.append(" "); + formatMethod((PsiMethod)owner, PsiSubstitutor.EMPTY, + SHOW_NAME | SHOW_FQ_NAME | SHOW_TYPE | SHOW_PARAMETERS | SHOW_FQ_CLASS_NAMES, + showParamName ? SHOW_NAME | SHOW_TYPE | SHOW_FQ_CLASS_NAMES : SHOW_TYPE | SHOW_FQ_CLASS_NAMES, maxParamsToShow, builder); + } + else if (owner instanceof PsiField) { + builder.append(" ").append(((PsiField)owner).getName()); + } + else if (owner instanceof PsiParameter) { + final PsiElement declarationScope = ((PsiParameter)owner).getDeclarationScope(); + if (!(declarationScope instanceof PsiMethod)) { + return null; + } + final PsiMethod psiMethod = (PsiMethod)declarationScope; + + builder.append(" "); + formatMethod(psiMethod, PsiSubstitutor.EMPTY, + SHOW_NAME | SHOW_FQ_NAME | SHOW_TYPE | SHOW_PARAMETERS | SHOW_FQ_CLASS_NAMES, + showParamName ? SHOW_NAME | SHOW_TYPE | SHOW_FQ_CLASS_NAMES : SHOW_TYPE | SHOW_FQ_CLASS_NAMES, maxParamsToShow, builder); + builder.append(" "); + + if (showParamName) { + formatVariable((PsiVariable)owner, SHOW_NAME, PsiSubstitutor.EMPTY, builder); + } + else { + builder.append(psiMethod.getParameterList().getParameterIndex((PsiParameter)owner)); + } + } + else { + return null; + } + return builder.toString(); + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiFormatUtilBase.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiFormatUtilBase.java new file mode 100644 index 00000000000..c508120c0ea --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/extAnnotations/PsiFormatUtilBase.java @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * 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.jetbrains.jet.lang.resolve.java.extAnnotations; + +import com.intellij.openapi.util.text.StringUtil; + +/** + * This class is copied from IDEA. + * This class should be eliminated when Kotlin will depend on IDEA 12.x (KT-2326) + * @author Evgeny Gerashchenko + * @since 6/26/12 + */ +@Deprecated +public abstract class PsiFormatUtilBase { + + public static final int SHOW_NAME = 0x0001; // variable, method, class + public static final int SHOW_TYPE = 0x0002; // variable, method + public static final int TYPE_AFTER = 0x0004; // variable, method + public static final int SHOW_MODIFIERS = 0x0008; // variable, method, class + public static final int MODIFIERS_AFTER = 0x0010; // variable, method, class + public static final int SHOW_REDUNDANT_MODIFIERS = 0x0020; // variable, method, class, modifier list + public static final int SHOW_PACKAGE_LOCAL = 0x0040; // variable, method, class, modifier list + public static final int SHOW_INITIALIZER = 0x0080; // variable + public static final int SHOW_PARAMETERS = 0x0100; // method + public static final int SHOW_THROWS = 0x0200; // method + public static final int SHOW_EXTENDS_IMPLEMENTS = 0x0400; // class + public static final int SHOW_FQ_NAME = 0x0800; // class, field, method + public static final int SHOW_CONTAINING_CLASS = 0x1000; // field, method + public static final int SHOW_FQ_CLASS_NAMES = 0x2000; // variable, method, class + public static final int JAVADOC_MODIFIERS_ONLY = 0x4000; // field, method, class + public static final int SHOW_ANONYMOUS_CLASS_VERBOSE = 0x8000; // class + public static final int SHOW_RAW_TYPE = 0x10000; //type + public static final int SHOW_RAW_NON_TOP_TYPE = 0x20000; + public static final int MAX_PARAMS_TO_SHOW = 7; + + protected static void appendSpaceIfNeeded(StringBuilder buffer) { + if (buffer.length() != 0 && !StringUtil.endsWithChar(buffer, ' ')) { + buffer.append(' '); + } + } +}