annotations.xml syntax checker appcode/171.960
authorAlexey Kudravtsev <cdr@intellij.com>
Sat, 12 Nov 2016 11:47:36 +0000 (14:47 +0300)
committerAlexey Kudravtsev <cdr@intellij.com>
Mon, 14 Nov 2016 12:10:42 +0000 (15:10 +0300)
13 files changed:
java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java
java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java [new file with mode: 0644]
java/jdkAnnotations/java/awt/annotations.xml
java/jdkAnnotations/java/awt/datatransfer/annotations.xml
java/jdkAnnotations/java/awt/event/annotations.xml
java/jdkAnnotations/java/lang/annotations.xml
java/jdkAnnotations/java/security/annotations.xml
java/jdkAnnotations/java/sql/annotations.xml
java/jdkAnnotations/java/util/annotations.xml
java/jdkAnnotations/javax/swing/annotations.xml
java/jdkAnnotations/javax/swing/plaf/basic/annotations.xml
java/jdkAnnotations/org/jdom/annotations.xml
platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java

index fb18c6f4652c8a2f493f26a92ed5db79b2700aa3..836a222dcdf52cec6c882877ef22f8201c0d83a3 100644 (file)
@@ -158,7 +158,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
   }
 
   @NotNull
-  private MostlySingularMultiMap<String, AnnotationData> getDataFromFile(@NotNull PsiFile file) {
+  MostlySingularMultiMap<String, AnnotationData> getDataFromFile(@NotNull PsiFile file) {
     Pair<MostlySingularMultiMap<String, AnnotationData>, Long> cached = myAnnotationFileToDataAndModStampCache.get(file);
     long fileModificationStamp = file.getModificationStamp();
     if (cached != null && cached.getSecond() == fileModificationStamp) {
@@ -336,16 +336,16 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
     throw new UnsupportedOperationException();
   }
 
-  protected void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName,
-                                          @NotNull PsiFile fromFile,
-                                          @NotNull List<PsiFile> annotationFiles) {
+  void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName,
+                                @NotNull PsiFile fromFile,
+                                @NotNull List<PsiFile> annotationFiles) {
     VirtualFile virtualFile = fromFile.getVirtualFile();
     if (virtualFile != null) {
       myExternalAnnotationsCache.put(virtualFile, annotationFiles);
     }
   }
 
-  private static class AnnotationData {
+  static class AnnotationData {
     private final String annotationClassFqName;
     private final String annotationParameters;
 
@@ -357,7 +357,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
     }
 
     @NotNull
-    private PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) {
+    PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) {
       PsiAnnotation a = myAnnotation;
       if (a == null) {
         String text = "@" + annotationClassFqName + (annotationParameters.isEmpty() ? "" : "(" + annotationParameters + ")");
diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java
new file mode 100644 (file)
index 0000000..e3da0c5
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2000-2016 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 com.intellij.codeInsight;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.application.ex.PathManagerEx;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiFormatUtil;
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.PsiTestUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MostlySingularMultiMap;
+import com.intellij.xml.util.XmlUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class ExternalAnnotationsManagerTest extends IdeaTestCase {
+  @Override
+  protected Sdk getTestProjectJdk() {
+    Sdk jdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    Sdk sdk = PsiTestUtil.addJdkAnnotations(jdk);
+    String home = jdk.getHomeDirectory().getParent().getPath();
+    String toolsPath = home + "/lib/tools.jar!/";
+    VfsRootAccess.allowRootAccess(getTestRootDisposable(), home);
+    VirtualFile toolsJar = JarFileSystem.getInstance().findFileByPath(toolsPath);
+
+    Sdk plusTools = PsiTestUtil.addRootsToJdk(sdk, OrderRootType.CLASSES, toolsJar);
+
+    Collection<String> utilClassPath = PathManager.getUtilClassPath();
+    VirtualFile[] files = utilClassPath.stream()
+      .map(path -> path.endsWith(".jar") ?
+                   JarFileSystem.getInstance() .findFileByPath(FileUtil.toSystemIndependentName(path) + "!/") :
+                   LocalFileSystem.getInstance() .findFileByPath(FileUtil.toSystemIndependentName(path)))
+      .toArray(VirtualFile[]::new);
+
+    Sdk result = PsiTestUtil.addRootsToJdk(plusTools, OrderRootType.CLASSES, files);
+    return result;
+  }
+
+  public void testBundledAnnotationXmls() {
+    String root = PathManagerEx.getCommunityHomePath() + "/java/jdkAnnotations";
+    findAnnotationsXmlAndCheck(root);
+  }
+
+  private void findAnnotationsXmlAndCheck(String root) {
+    VirtualFile jdkAnnoRoot = LocalFileSystem.getInstance().findFileByPath(root);
+    VfsUtilCore.visitChildrenRecursively(jdkAnnoRoot, new VirtualFileVisitor() {
+                                           @Override
+                                           public boolean visitFile(@NotNull VirtualFile file) {
+                                             if (file.getName().equals("annotations.xml")) {
+                                               check(file);
+                                             }
+                                             return true;
+                                           }
+                                         });
+  }
+
+  //  some android classes are missing in IDEA, e.g. android.support.annotation.NonNull
+  public void _testAndroidAnnotationsXml() {
+    VirtualFile lib = LocalFileSystem.getInstance().findFileByPath(PathManagerEx.getCommunityHomePath() + "/android/android/lib");
+    VirtualFile[] androidJars = Arrays.stream(lib.getChildren())
+      .map(file -> file.getName().endsWith(".jar") ?
+                   JarFileSystem.getInstance().getJarRootForLocalFile(file) :
+                   file)
+      .toArray(VirtualFile[]::new);
+
+    ApplicationManager.getApplication().runWriteAction(() -> ProjectRootManager.getInstance(getProject())
+      .setProjectSdk(PsiTestUtil.addRootsToJdk(getTestProjectJdk(), OrderRootType.CLASSES, androidJars)));
+
+    String root = PathManagerEx.getCommunityHomePath() + "/android/android/annotations";
+    findAnnotationsXmlAndCheck(root);
+  }
+
+  private void check(VirtualFile file) {
+    //System.out.println("file = " + file);
+    ExternalAnnotationsManagerImpl manager = (ExternalAnnotationsManagerImpl)ExternalAnnotationsManager.getInstance(getProject());
+    PsiFile psiFile = getPsiManager().findFile(file);
+    MostlySingularMultiMap<String, BaseExternalAnnotationsManager.AnnotationData> map = manager.getDataFromFile(psiFile);
+    for (String externalName : map.keySet()) {
+      checkExternalName(psiFile, externalName);
+
+      // 'annotation name="org.jetbrains.annotations.NotNull"' should have FQN
+      for (BaseExternalAnnotationsManager.AnnotationData annotationData : map.get(externalName)) {
+        PsiAnnotation annotation = annotationData.getAnnotation(manager);
+        String nameText = annotation.getNameReferenceElement().getText();
+        assertClassFqn(nameText, psiFile, externalName);
+      }
+    }
+  }
+
+  private PsiClass assertClassFqn(String text, PsiFile psiFile, String externalName) {
+    if (!PsiNameHelper.getInstance(getProject()).isQualifiedName(text) || !text.contains(".")) {
+      fail("'" + text + "' doesn't seem like a FQN", psiFile, externalName);
+    }
+
+    PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass(text, GlobalSearchScope.allScope(getProject()));
+    if (aClass == null) {
+      fail("'" + text + "' doesn't resolve to a class", psiFile, externalName);
+    }
+    return aClass;
+  }
+
+  private static void fail(String error, PsiFile psiFile, String externalName) {
+    int offset = psiFile.getText().indexOf(XmlUtil.escape(externalName));
+    int line = PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile).getLineNumber(offset);
+    fail(error + "\nFile: " + psiFile.getVirtualFile().getPath() + ":" + (line+1) + " (offset: "+offset+")");
+  }
+
+  private void checkExternalName(PsiFile psiFile, String externalName) {
+    // 'item name="java.lang.ClassLoader java.net.URL getResource(java.lang.String) 0"' should have all FQNs
+    String unescaped = StringUtil.unescapeXml(externalName);
+    List<String> words = StringUtil.split(unescaped, " ");
+    String className = words.get(0);
+    PsiClass aClass = assertClassFqn(className, psiFile, externalName);
+    if (words.size() == 1) return;
+
+    String rest = unescaped.substring(className.length() + " ".length());
+
+    if (rest.indexOf('(') == -1) {
+      // field
+      String field = StringUtil.trim(rest);
+      PsiField psiField = aClass.findFieldByName(field, false);
+      if (psiField == null) {
+        fail("Field '"+field+"' not found in class '"+aClass.getQualifiedName()+"'", psiFile, externalName);
+      }
+      return;
+    }
+    String methodName = ContainerUtil.getLastItem(StringUtil.getWordsIn(rest.substring(0, rest.indexOf('('))));
+
+    String methodSignature = rest.substring(0, rest.indexOf(')') + 1);
+    String methodExternalName = className + " " + methodSignature;
+
+    List<PsiMethod> methods = Arrays.stream(aClass.getMethods())
+      .filter(method -> methodExternalName.equals(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE)))
+      .collect(Collectors.toList());
+    boolean found = !methods.isEmpty();
+    if (!found) {
+      List<String> candidates = Arrays.stream(aClass.findMethodsByName(methodName, false))
+        .map(method -> XmlUtil.escape(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE)))
+        .collect(Collectors.toList());
+      String additionalMsg = candidates.isEmpty() ? "" : "\nMaybe you have meant one of these methods instead:\n"+StringUtil.join(candidates, "\n")+"\n";
+      fail("This method was not found in class '"+aClass.getQualifiedName()+"':\n"+"'"+methodSignature+"'"+additionalMsg, psiFile, externalName);
+    }
+
+    String parameterNumberText = StringUtil.trim(rest.substring(rest.indexOf(')') + 1));
+    if (parameterNumberText.isEmpty()) return;
+
+    try {
+      int paramNumber = Integer.parseInt(parameterNumberText);
+      PsiMethod method = methods.get(0);
+      if (method.getParameterList().getParametersCount() <= paramNumber) {
+        fail("Parameter number '"+paramNumber+"' is too big for a method '"+methodSignature+"'", psiFile, externalName);
+      }
+    }
+    catch (NumberFormatException e) {
+      fail("Parameter number is not an integer: '"+parameterNumberText+"'", psiFile, externalName);
+    }
+  }
+}
index 50d1f16ca819266c9001a7f9e70e09f45f83473e..74edac03f2b42b8b1a8da54942c36347b6581dfe 100644 (file)
@@ -65,9 +65,6 @@
       <val name="intValues" val="{java.awt.BasicStroke.JOIN_BEVEL, java.awt.BasicStroke.JOIN_MITER, java.awt.BasicStroke.JOIN_ROUND}" />
     </annotation>
   </item>
-  <item name="java.awt.CardLayout void show(Container parent, java.lang.String) 1">
-    <annotation name="org.jetbrains.annotations.NonNls" />
-  </item>
   <item name="java.awt.CardLayout void show(java.awt.Container, java.lang.String) 1">
     <annotation name="org.jetbrains.annotations.NonNls" />
   </item>
index 329dbd0b61bf195f52e53fbb3a5978361318dad8..afc6ca191604947ea7362ff3ecec090e334bfb93 100644 (file)
@@ -2,7 +2,7 @@
   <item name='java.awt.datatransfer.DataFlavor DataFlavor(java.lang.String) 0'>
     <annotation name='org.jetbrains.annotations.NonNls'/>
   </item>
-  <item name='java.awt.datatransfer.DataFlavor DataFlavor(java.lang.String, java.lang.String,java.lang.ClassLoader) 0'>
+  <item name='java.awt.datatransfer.DataFlavor DataFlavor(java.lang.String, java.lang.String, java.lang.ClassLoader) 0'>
     <annotation name='org.jetbrains.annotations.NonNls'/>
   </item>
 </root>
index 8808ee8ae40c9019f37419fb0016f5ff835f8e5e..00a5129e2911d4fefcd5598b4869bc74dced4d57 100644 (file)
@@ -30,7 +30,7 @@
       <val name="flags" val="{java.awt.event.HierarchyEvent.PARENT_CHANGED,java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED,java.awt.event.HierarchyEvent.SHOWING_CHANGED}" />
     </annotation>
   </item>
-  <item name="java.awt.event.HierarchyEvent int getChangeFlags()">
+  <item name="java.awt.event.HierarchyEvent long getChangeFlags()">
     <annotation name="org.intellij.lang.annotations.MagicConstant">
       <val name="flags" val="{java.awt.event.HierarchyEvent.PARENT_CHANGED,java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED,java.awt.event.HierarchyEvent.SHOWING_CHANGED}" />
     </annotation>
index 7ce0469d41b846db9ccf37c4701681e7c3949504..572588fb1356867a380a59e79b4e9d76f2282ca3 100644 (file)
   <item name="java.lang.Class java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class&lt;?&gt;...) 0">
     <annotation name="org.jetbrains.annotations.NonNls" />
   </item>
-  <item name="java.lang.Class java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class...) 0">
-    <annotation name="org.jetbrains.annotations.NonNls" />
-  </item>
-  <item name="java.lang.ClassLoader InputStream getResourceAsStream(java.lang.String) 0">
-    <annotation name="org.jetbrains.annotations.NonNls" />
-  </item>
   <item name="java.lang.ClassLoader java.io.InputStream getResourceAsStream(java.lang.String) 0">
     <annotation name="org.jetbrains.annotations.NonNls" />
   </item>
@@ -70,9 +64,6 @@
   <item name="java.lang.Readable int read(java.nio.CharBuffer) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
-  <item name="java.lang.Runtime Process exec(java.lang.String[]) 0">
-    <annotation name="org.jetbrains.annotations.NonNls" />
-  </item>
   <item name="java.lang.Runtime java.lang.Process exec(java.lang.String[]) 0">
     <annotation name="org.jetbrains.annotations.NonNls" />
   </item>
index 96f7fd9febacfdde9337b6e3b48002ae562e06a4..21f4ccae9a49da55d721215686161aca49c9625f 100644 (file)
@@ -1,5 +1,5 @@
 <root>
-  <item name='java.security.MessageDigest MessageDigest java.security.getInstance(java.lang.String) 0'>
+  <item name='java.security.MessageDigest java.security.MessageDigest getInstance(java.lang.String) 0'>
     <annotation name='org.jetbrains.annotations.NonNls'/>
   </item>
 </root>
index 59c93eb08f23c034e8e686ec66fe1f6f6e3fd25d..b9c1e4a911409ac278488d0d731c7eb5e8d71f53 100644 (file)
       <val name="intValues" val="{java.sql.Statement.RETURN_GENERATED_KEYS, java.sql.Statement.NO_GENERATED_KEYS}" />
     </annotation>
   </item>
-  <item name="java.sql.Statement int execute(java.lang.String, int) 1">
+  <item name="java.sql.Statement boolean execute(java.lang.String, int) 1">
     <annotation name="org.intellij.lang.annotations.MagicConstant">
       <val name="intValues" val="{java.sql.Statement.RETURN_GENERATED_KEYS, java.sql.Statement.NO_GENERATED_KEYS}" />
     </annotation>
index cbadb8b58ede8c9c6f6a2629a2e9995f7e3472c0..9301024eb2ab2e57d9e58f795b2401d0bcf5e2c0 100644 (file)
       <val name="targetIsContainer" val="true"/>
     </annotation>
   </item>
-  <item name="java.util.Collection boolean containsAll(java.util.Collection&lt;?&gt; c) 0">
-    <annotation name="org.jetbrains.annotations.NotNull" />
-  </item>
   <item name="java.util.Collection boolean containsAll(java.util.Collection&lt;?&gt;) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
-  <item name="java.util.Collection boolean removeAll(java.util.Collection&lt;?&gt; c) 0">
-    <annotation name="org.jetbrains.annotations.NotNull" />
-  </item>
   <item name="java.util.Collection boolean removeAll(java.util.Collection&lt;?&gt;) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
-  <item name="java.util.Collection boolean retainAll(java.util.Collection&lt;?&gt; c) 0">
-    <annotation name="org.jetbrains.annotations.NotNull" />
-  </item>
   <item name="java.util.Collection boolean retainAll(java.util.Collection&lt;?&gt;) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
       <val name="targetIsContainer" val="true"/>
     </annotation>
   </item>
-  <item name="java.util.List List&lt;E&gt; subList(int fromIndex, int toIndex)">
-    <annotation name="org.jetbrains.annotations.NotNull" />
-  </item>
-  <item name="java.util.List ListIterator&lt;E&gt; listIterator()">
-    <annotation name="org.jetbrains.annotations.NotNull" />
-  </item>
   <item name="java.util.List T[] toArray(T[])">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
index 6d21cc9622e28f90319f943370785130783cde4e..2dd236b883b6d915a9b7df04446bd56034b57d05 100644 (file)
       <val name="intValues" val="{javax.swing.JTabbedPane.WRAP_TAB_LAYOUT, javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT}" />
     </annotation>
   </item>
+  <item name="javax.swing.JTabbedPane int getTabLayoutPolicy()">
+    <annotation name="org.intellij.lang.annotations.MagicConstant">
+      <val name="intValues" val="{javax.swing.JTabbedPane.WRAP_TAB_LAYOUT, javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT}" />
+    </annotation>
+  </item>
+  <item name="javax.swing.JTabbedPane int getTabPlacement()">
+    <annotation name="org.intellij.lang.annotations.MagicConstant">
+      <val name="intValues" val="{javax.swing.SwingConstants.TOP, javax.swing.SwingConstants.BOTTOM, javax.swing.SwingConstants.LEFT, javax.swing.SwingConstants.RIGHT}" />
+    </annotation>
+  </item>
   <item name="javax.swing.JTable JTable(java.lang.Object[][], java.lang.Object[]) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
   <item name="javax.swing.JTable int columnAtPoint(java.awt.Point) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
-  <item name="javax.swing.JTable int getTabLayoutPolicy()">
-    <annotation name="org.intellij.lang.annotations.MagicConstant">
-      <val name="intValues" val="{javax.swing.JTabbedPane.WRAP_TAB_LAYOUT, javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT}" />
-    </annotation>
-  </item>
-  <item name="javax.swing.JTable int getTabPlacement()">
-    <annotation name="org.intellij.lang.annotations.MagicConstant">
-      <val name="intValues" val="{javax.swing.SwingConstants.TOP, javax.swing.SwingConstants.BOTTOM, javax.swing.SwingConstants.LEFT, javax.swing.SwingConstants.RIGHT}" />
-    </annotation>
-  </item>
   <item name="javax.swing.JTable int rowAtPoint(java.awt.Point) 0">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
index 420b4dcf6c758fef2ec68808c9a4892ebfcf3d7a..f51d4d8ca4315162efd0942bbb44113d1d7cdac5 100644 (file)
@@ -10,7 +10,7 @@
       <val name="intValues" val="{javax.swing.SwingConstants.NORTH,javax.swing.SwingConstants.SOUTH,javax.swing.SwingConstants.EAST,javax.swing.SwingConstants.WEST}" />
     </annotation>
   </item>
-  <item name="javax.swing.plaf.basic.BasicArrowButton void getDirection()">
+  <item name="javax.swing.plaf.basic.BasicArrowButton int getDirection()">
     <annotation name="org.intellij.lang.annotations.MagicConstant">
       <val name="intValues" val="{javax.swing.SwingConstants.NORTH,javax.swing.SwingConstants.SOUTH,javax.swing.SwingConstants.EAST,javax.swing.SwingConstants.WEST}" />
     </annotation>
index 02739c14c0c06db9877bd34adc21dee135aa8ceb..2171c711208e2c6d8302c6b0d9125dacdda4b845 100644 (file)
   <item name="org.jdom.Element Element(java.lang.String) 0">
     <annotation name="org.jetbrains.annotations.NonNls" />
   </item>
-  <item name="org.jdom.Element java.util.List getChildren()">
+  <item name="org.jdom.Element java.util.List&lt;org.jdom.Element&gt; getChildren()">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
-  <item name="org.jdom.Element java.util.List getChildren(java.lang.String)">
+  <item name="org.jdom.Element java.util.List&lt;org.jdom.Element&gt; getChildren(java.lang.String)">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
-  <item name="org.jdom.Element java.util.List getChildren(java.lang.String, org.jdom.Namespace)">
+  <item name="org.jdom.Element java.util.List&lt;org.jdom.Element&gt; getChildren(java.lang.String, org.jdom.Namespace)">
     <annotation name="org.jetbrains.annotations.NotNull" />
   </item>
   <item name='org.jdom.Element java.util.List&lt;org.jdom.Content&gt; getContent()'>
index 9a1404a94bc0e5b40588020bd1c4b18c88988b48..50f5c6b04007b1e870f5c97b0273145d565fba08 100644 (file)
@@ -373,6 +373,14 @@ public class PsiTestUtil {
   public static Sdk addJdkAnnotations(@NotNull Sdk sdk) {
     String path = FileUtil.toSystemIndependentName(PlatformTestUtil.getCommunityPath()) + "/java/jdkAnnotations";
     VirtualFile root = LocalFileSystem.getInstance().findFileByPath(path);
+    return addRootsToJdk(sdk, AnnotationOrderRootType.getInstance(), root);
+  }
+
+  @NotNull
+  @Contract(pure=true)
+  public static Sdk addRootsToJdk(@NotNull Sdk sdk,
+                                  @NotNull OrderRootType rootType,
+                                  @NotNull VirtualFile... roots) {
     Sdk clone;
     try {
       clone = (Sdk)sdk.clone();
@@ -381,7 +389,9 @@ public class PsiTestUtil {
       throw new RuntimeException(e);
     }
     SdkModificator sdkModificator = clone.getSdkModificator();
-    sdkModificator.addRoot(root, AnnotationOrderRootType.getInstance());
+    for (VirtualFile root : roots) {
+      sdkModificator.addRoot(root, rootType);
+    }
     sdkModificator.commitChanges();
     return clone;
   }