private void keys(Node x, Queue<Key> queue, Key lo, Key hi) { if (x == null) return; int compLo = lo.compareTo(x.key); int compHi = hi.compareTo(x.key); if (compLo < 0) keys(x.left, queue, lo, hi); if (compLo <= 0 && compHi >= 0) queue.enqueue(x.key); if (compHi > 0) keys(x.right, queue, lo, hi); }
private Node delete(Node x, Key key) { if (x == null) return null; int comp = key.compareTo(x.key); if (comp < 0) x.left = delete(x.left, key); else if (comp > 0) x.right = delete(x.right, key); else { if (x.left == null) { if (x.pred != null) x.pred.succ = x.succ; if (x.succ != null) x.succ.pred = x.pred; return x.right; } if (x.right == null) { if (x.pred != null) x.pred.succ = x.succ; if (x.succ != null) x.succ.pred = x.pred; return x.left; } Node t = max(x.left); t.left = deleteMaxWithOutDeleteThread(x.left); t.right = x.right; if (x.pred != null) x.pred.succ = x.succ; if (x.succ != null) x.succ.pred = x.pred; x = t; } x.N = size(x.left) + size(x.right) + 1; x.height = Math.max(height(x.left), height(x.right)) + 1; x.avgCompares = (avgCompares(x.left) * size(x.left) + avgCompares(x.right) * size(x.right) + size(x)) / size(x); return x; }
private int rank(Node x, Key key) { if (x == null) return 0; int comp = key.compareTo(x.key); if (comp == 0) return size(x.left); else if (comp < 0) return rank(x.left, key); else return size(x.left) + 1 + rank(x.right, key); }
private Node put(Node x, Key key, Value val) { if (x == null) { Node pre = floor(root, key); Node nex = ceiling(root, key); lastest = new Node(key, val, 1, 1, 1); if (pre != null) pre.succ = lastest; if (nex != null) nex.pred = lastest; lastest.succ = nex; lastest.pred = pre; return lastest; } int comp = key.compareTo(x.key); if (comp < 0) x.left = put(x.left, key, val); else if (comp > 0) x.right = put(x.right, key, val); else { lastest = x; x.value = val; } x.N = size(x.right) + size(x.left) + 1; x.height = Math.max(height(x.left), height(x.right)) + 1; x.avgCompares = (avgCompares(x.left) * size(x.left) + avgCompares(x.right) * size(x.right) + size(x)) / size(x); return x; }
/* private Node get(Node x, Key key) //recursive implementation { if(x == null) return null; int com = key.compareTo(x.key); if(com < 0) return get(x.left, key); else if(com > 0) return get(x.right, key); else return x; }*/ private Node get(Node x, Key key) { while (x != null) { int comp = key.compareTo(x.key); if (comp < 0) x = x.left; else if (comp > 0) x = x.right; else return x; } return null; }
private Node floor(Node x, Key key) { if (x == null) return null; int comp = key.compareTo(x.key); if (comp < 0) return floor(x.left, key); else if (comp > 0) { Node res = floor(x.right, key); if (res != null) return res; else return x; } else return x; }
private Node ceiling(Node x, Key key) { if (x == null) return null; int comp = key.compareTo(x.key); if (comp > 0) return ceiling(x.right, key); else if (comp == 0) return x; else { Node res = ceiling(x.left, key); if (res == null) return x; else return res; } }
/** @author peter */ @SuppressWarnings("UseOfSystemOutOrSystemErr") public abstract class UsefulTestCase extends TestCase { public static final boolean IS_UNDER_TEAMCITY = System.getenv("TEAMCITY_VERSION") != null; public static final String IDEA_MARKER_CLASS = "com.intellij.openapi.components.impl.stores.IdeaProjectStoreImpl"; public static final String TEMP_DIR_MARKER = "unitTest_"; protected static boolean OVERWRITE_TESTDATA = false; private static final String DEFAULT_SETTINGS_EXTERNALIZED; private static final Random RNG = new SecureRandom(); private static final String ORIGINAL_TEMP_DIR = FileUtil.getTempDirectory(); protected final Disposable myTestRootDisposable = new Disposable() { @Override public void dispose() {} @Override public String toString() { String testName = getTestName(false); return UsefulTestCase.this.getClass() + (StringUtil.isEmpty(testName) ? "" : ".test" + testName); } }; protected static String ourPathToKeep = null; private CodeStyleSettings myOldCodeStyleSettings; private String myTempDir; protected static final Key<String> CREATION_PLACE = Key.create("CREATION_PLACE"); static { // Radar #5755208: Command line Java applications need a way to launch without a Dock icon. System.setProperty("apple.awt.UIElement", "true"); try { CodeInsightSettings defaultSettings = new CodeInsightSettings(); Element oldS = new Element("temp"); defaultSettings.writeExternal(oldS); DEFAULT_SETTINGS_EXTERNALIZED = JDOMUtil.writeElement(oldS, "\n"); } catch (Exception e) { throw new RuntimeException(e); } } protected boolean shouldContainTempFiles() { return true; } @Override protected void setUp() throws Exception { super.setUp(); if (shouldContainTempFiles()) { String testName = getTestName(true); if (StringUtil.isEmptyOrSpaces(testName)) testName = ""; testName = new File(testName).getName(); // in case the test name contains file separators myTempDir = FileUtil.toSystemDependentName( ORIGINAL_TEMP_DIR + "/" + TEMP_DIR_MARKER + testName + "_" + RNG.nextInt(1000)); FileUtil.resetCanonicalTempPathCache(myTempDir); } //noinspection AssignmentToStaticFieldFromInstanceMethod DocumentImpl.CHECK_DOCUMENT_CONSISTENCY = !isPerformanceTest(); } @Override protected void tearDown() throws Exception { try { Disposer.dispose(myTestRootDisposable); cleanupSwingDataStructures(); cleanupDeleteOnExitHookList(); } finally { if (shouldContainTempFiles()) { FileUtil.resetCanonicalTempPathCache(ORIGINAL_TEMP_DIR); if (ourPathToKeep != null && FileUtil.isAncestor(myTempDir, ourPathToKeep, false)) { File[] files = new File(myTempDir).listFiles(); if (files != null) { for (File file : files) { if (!FileUtil.pathsEqual(file.getPath(), ourPathToKeep)) { FileUtil.delete(file); } } } } else { FileUtil.delete(new File(myTempDir)); } } } UIUtil.removeLeakingAppleListeners(); super.tearDown(); } private static final Set<String> DELETE_ON_EXIT_HOOK_DOT_FILES; private static final Class DELETE_ON_EXIT_HOOK_CLASS; static { Class<?> aClass = null; Set<String> files = null; try { aClass = Class.forName("java.io.DeleteOnExitHook"); files = ReflectionUtil.getField(aClass, null, Set.class, "files"); } catch (Exception ignored) { } DELETE_ON_EXIT_HOOK_CLASS = aClass; DELETE_ON_EXIT_HOOK_DOT_FILES = files; } public static void cleanupDeleteOnExitHookList() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { // try to reduce file set retained by java.io.DeleteOnExitHook List<String> list; synchronized (DELETE_ON_EXIT_HOOK_CLASS) { if (DELETE_ON_EXIT_HOOK_DOT_FILES.isEmpty()) return; list = new ArrayList<String>(DELETE_ON_EXIT_HOOK_DOT_FILES); } for (int i = list.size() - 1; i >= 0; i--) { String path = list.get(i); if (FileSystemUtil.getAttributes(path) == null || new File(path).delete()) { synchronized (DELETE_ON_EXIT_HOOK_CLASS) { DELETE_ON_EXIT_HOOK_DOT_FILES.remove(path); } } } } private static void cleanupSwingDataStructures() throws Exception { Class<?> aClass = Class.forName("javax.swing.KeyboardManager"); Method get = aClass.getMethod("getCurrentManager"); get.setAccessible(true); Object manager = get.invoke(null); { Field mapF = aClass.getDeclaredField("componentKeyStrokeMap"); mapF.setAccessible(true); Object map = mapF.get(manager); ((Map) map).clear(); } { Field mapF = aClass.getDeclaredField("containerMap"); mapF.setAccessible(true); Object map = mapF.get(manager); ((Map) map).clear(); } } protected CompositeException checkForSettingsDamage() throws Exception { Application app = ApplicationManager.getApplication(); if (isPerformanceTest() || app == null || app instanceof MockApplication) { return new CompositeException(); } CodeStyleSettings oldCodeStyleSettings = myOldCodeStyleSettings; myOldCodeStyleSettings = null; return doCheckForSettingsDamage(oldCodeStyleSettings, getCurrentCodeStyleSettings()); } public static CompositeException doCheckForSettingsDamage( @NotNull CodeStyleSettings oldCodeStyleSettings, @NotNull CodeStyleSettings currentCodeStyleSettings) throws Exception { CompositeException result = new CompositeException(); final CodeInsightSettings settings = CodeInsightSettings.getInstance(); try { Element newS = new Element("temp"); settings.writeExternal(newS); Assert.assertEquals( "Code insight settings damaged", DEFAULT_SETTINGS_EXTERNALIZED, JDOMUtil.writeElement(newS, "\n")); } catch (AssertionError error) { CodeInsightSettings clean = new CodeInsightSettings(); Element temp = new Element("temp"); clean.writeExternal(temp); settings.loadState(temp); result.add(error); } currentCodeStyleSettings.getIndentOptions(StdFileTypes.JAVA); try { checkSettingsEqual( oldCodeStyleSettings, currentCodeStyleSettings, "Code style settings damaged"); } catch (AssertionError e) { result.add(e); } finally { currentCodeStyleSettings.clearCodeStyleSettings(); } try { InplaceRefactoring.checkCleared(); } catch (AssertionError e) { result.add(e); } try { StartMarkAction.checkCleared(); } catch (AssertionError e) { result.add(e); } return result; } protected void storeSettings() { if (!isPerformanceTest() && ApplicationManager.getApplication() != null) { myOldCodeStyleSettings = getCurrentCodeStyleSettings().clone(); myOldCodeStyleSettings.getIndentOptions(StdFileTypes.JAVA); } } protected CodeStyleSettings getCurrentCodeStyleSettings() { if (CodeStyleSchemes.getInstance().getCurrentScheme() == null) return new CodeStyleSettings(); return CodeStyleSettingsManager.getInstance().getCurrentSettings(); } public Disposable getTestRootDisposable() { return myTestRootDisposable; } @Override protected void runTest() throws Throwable { final Throwable[] throwables = new Throwable[1]; Runnable runnable = new Runnable() { @Override public void run() { try { UsefulTestCase.super.runTest(); } catch (InvocationTargetException e) { e.fillInStackTrace(); throwables[0] = e.getTargetException(); } catch (IllegalAccessException e) { e.fillInStackTrace(); throwables[0] = e; } catch (Throwable e) { throwables[0] = e; } } }; invokeTestRunnable(runnable); if (throwables[0] != null) { throw throwables[0]; } } protected boolean shouldRunTest() { return PlatformTestUtil.canRunTest(getClass()); } public static void edt(Runnable r) { UIUtil.invokeAndWaitIfNeeded(r); } protected void invokeTestRunnable(@NotNull Runnable runnable) throws Exception { UIUtil.invokeAndWaitIfNeeded(runnable); // runnable.run(); } protected void defaultRunBare() throws Throwable { super.runBare(); } @Override public void runBare() throws Throwable { if (!shouldRunTest()) return; if (runInDispatchThread()) { final Throwable[] exception = {null}; UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { try { defaultRunBare(); } catch (Throwable tearingDown) { if (exception[0] == null) exception[0] = tearingDown; } } }); if (exception[0] != null) throw exception[0]; } else { defaultRunBare(); } } protected boolean runInDispatchThread() { return true; } @NonNls public static String toString(Iterable<?> collection) { if (!collection.iterator().hasNext()) { return "<empty>"; } final StringBuilder builder = new StringBuilder(); for (final Object o : collection) { if (o instanceof THashSet) { builder.append(new TreeSet<Object>((THashSet) o)); } else { builder.append(o); } builder.append("\n"); } return builder.toString(); } public static <T> void assertOrderedEquals(T[] actual, T... expected) { assertOrderedEquals(Arrays.asList(actual), expected); } public static <T> void assertOrderedEquals(Iterable<T> actual, T... expected) { assertOrderedEquals(null, actual, expected); } public static void assertOrderedEquals(@NotNull byte[] actual, @NotNull byte[] expected) { assertEquals(actual.length, expected.length); for (int i = 0; i < actual.length; i++) { byte a = actual[i]; byte e = expected[i]; assertEquals("not equals at index: " + i, e, a); } } public static <T> void assertOrderedEquals( final String errorMsg, @NotNull Iterable<T> actual, @NotNull T... expected) { Assert.assertNotNull(actual); Assert.assertNotNull(expected); assertOrderedEquals(errorMsg, actual, Arrays.asList(expected)); } public static <T> void assertOrderedEquals( final Iterable<? extends T> actual, final Collection<? extends T> expected) { assertOrderedEquals(null, actual, expected); } public static <T> void assertOrderedEquals( final String erroMsg, final Iterable<? extends T> actual, final Collection<? extends T> expected) { ArrayList<T> list = new ArrayList<T>(); for (T t : actual) { list.add(t); } if (!list.equals(new ArrayList<T>(expected))) { String expectedString = toString(expected); String actualString = toString(actual); Assert.assertEquals(erroMsg, expectedString, actualString); Assert.fail( "Warning! 'toString' does not reflect the difference.\nExpected: " + expectedString + "\nActual: " + actualString); } } public static <T> void assertOrderedCollection(T[] collection, @NotNull Consumer<T>... checkers) { Assert.assertNotNull(collection); assertOrderedCollection(Arrays.asList(collection), checkers); } public static <T> void assertSameElements(T[] collection, T... expected) { assertSameElements(Arrays.asList(collection), expected); } public static <T> void assertSameElements(Collection<? extends T> collection, T... expected) { assertSameElements(collection, Arrays.asList(expected)); } public static <T> void assertSameElements( Collection<? extends T> collection, Collection<T> expected) { assertSameElements(null, collection, expected); } public static <T> void assertSameElements( String message, Collection<? extends T> collection, Collection<T> expected) { assertNotNull(collection); assertNotNull(expected); if (collection.size() != expected.size() || !new HashSet<T>(expected).equals(new HashSet<T>(collection))) { Assert.assertEquals(message, toString(expected, "\n"), toString(collection, "\n")); Assert.assertEquals(message, new HashSet<T>(expected), new HashSet<T>(collection)); } } public <T> void assertContainsOrdered(Collection<? extends T> collection, T... expected) { assertContainsOrdered(collection, Arrays.asList(expected)); } public <T> void assertContainsOrdered( Collection<? extends T> collection, Collection<T> expected) { ArrayList<T> copy = new ArrayList<T>(collection); copy.retainAll(expected); assertOrderedEquals(toString(collection), copy, expected); } public <T> void assertContainsElements(Collection<? extends T> collection, T... expected) { assertContainsElements(collection, Arrays.asList(expected)); } public <T> void assertContainsElements( Collection<? extends T> collection, Collection<T> expected) { ArrayList<T> copy = new ArrayList<T>(collection); copy.retainAll(expected); assertSameElements(toString(collection), copy, expected); } public static String toString(Object[] collection, String separator) { return toString(Arrays.asList(collection), separator); } public <T> void assertDoesntContain(Collection<? extends T> collection, T... notExpected) { assertDoesntContain(collection, Arrays.asList(notExpected)); } public <T> void assertDoesntContain( Collection<? extends T> collection, Collection<T> notExpected) { ArrayList<T> expected = new ArrayList<T>(collection); expected.removeAll(notExpected); assertSameElements(collection, expected); } public static String toString(Collection<?> collection, String separator) { List<String> list = ContainerUtil.map2List( collection, new Function<Object, String>() { @Override public String fun(final Object o) { return String.valueOf(o); } }); Collections.sort(list); StringBuilder builder = new StringBuilder(); boolean flag = false; for (final String o : list) { if (flag) { builder.append(separator); } builder.append(o); flag = true; } return builder.toString(); } public static <T> void assertOrderedCollection( Collection<? extends T> collection, Consumer<T>... checkers) { Assert.assertNotNull(collection); if (collection.size() != checkers.length) { Assert.fail(toString(collection)); } int i = 0; for (final T actual : collection) { try { checkers[i].consume(actual); } catch (AssertionFailedError e) { System.out.println(i + ": " + actual); throw e; } i++; } } public static <T> void assertUnorderedCollection(T[] collection, Consumer<T>... checkers) { assertUnorderedCollection(Arrays.asList(collection), checkers); } public static <T> void assertUnorderedCollection( Collection<? extends T> collection, Consumer<T>... checkers) { Assert.assertNotNull(collection); if (collection.size() != checkers.length) { Assert.fail(toString(collection)); } Set<Consumer<T>> checkerSet = new HashSet<Consumer<T>>(Arrays.asList(checkers)); int i = 0; Throwable lastError = null; for (final T actual : collection) { boolean flag = true; for (final Consumer<T> condition : checkerSet) { Throwable error = accepts(condition, actual); if (error == null) { checkerSet.remove(condition); flag = false; break; } else { lastError = error; } } if (flag) { lastError.printStackTrace(); Assert.fail("Incorrect element(" + i + "): " + actual); } i++; } } private static <T> Throwable accepts(final Consumer<T> condition, final T actual) { try { condition.consume(actual); return null; } catch (Throwable e) { return e; } } public static <T> T assertInstanceOf(Object o, Class<T> aClass) { Assert.assertNotNull("Expected instance of: " + aClass.getName() + " actual: " + null, o); Assert.assertTrue( "Expected instance of: " + aClass.getName() + " actual: " + o.getClass().getName(), aClass.isInstance(o)); @SuppressWarnings("unchecked") T t = (T) o; return t; } public static <T> T assertOneElement(Collection<T> collection) { Assert.assertNotNull(collection); Assert.assertEquals(toString(collection), 1, collection.size()); return collection.iterator().next(); } public static <T> T assertOneElement(T[] ts) { Assert.assertNotNull(ts); Assert.assertEquals(Arrays.asList(ts).toString(), 1, ts.length); return ts[0]; } public static <T> void assertOneOf(T value, T... values) { boolean found = false; for (T v : values) { if (value == v || value != null && value.equals(v)) { found = true; } } Assert.assertTrue(value + " should be equal to one of " + Arrays.toString(values), found); } public static void printThreadDump() { PerformanceWatcher.dumpThreadsToConsole("Thread dump:"); } public static void assertEmpty(final Object[] array) { assertOrderedEquals(array); } public static void assertNotEmpty(final Collection<?> collection) { if (collection == null) return; assertTrue(!collection.isEmpty()); } public static void assertEmpty(final Collection<?> collection) { assertEmpty(collection.toString(), collection); } public static void assertNullOrEmpty(final Collection<?> collection) { if (collection == null) return; assertEmpty(null, collection); } public static void assertEmpty(final String s) { assertTrue(s, StringUtil.isEmpty(s)); } public static <T> void assertEmpty(final String errorMsg, final Collection<T> collection) { assertOrderedEquals(errorMsg, collection); } public static void assertSize(int expectedSize, final Object[] array) { assertEquals(toString(Arrays.asList(array)), expectedSize, array.length); } public static void assertSize(int expectedSize, final Collection<?> c) { assertEquals(toString(c), expectedSize, c.size()); } protected <T extends Disposable> T disposeOnTearDown(final T disposable) { Disposer.register(myTestRootDisposable, disposable); return disposable; } public static void assertSameLines(String expected, String actual) { String expectedText = StringUtil.convertLineSeparators(expected.trim()); String actualText = StringUtil.convertLineSeparators(actual.trim()); Assert.assertEquals(expectedText, actualText); } public static void assertExists(File file) { assertTrue("File should exist " + file, file.exists()); } public static void assertDoesntExist(File file) { assertFalse("File should not exist " + file, file.exists()); } protected String getTestName(boolean lowercaseFirstLetter) { String name = getName(); return getTestName(name, lowercaseFirstLetter); } public static String getTestName(String name, boolean lowercaseFirstLetter) { if (name == null) { return ""; } name = StringUtil.trimStart(name, "test"); if (StringUtil.isEmpty(name)) { return ""; } return lowercaseFirstLetter(name, lowercaseFirstLetter); } public static String lowercaseFirstLetter(String name, boolean lowercaseFirstLetter) { if (lowercaseFirstLetter && !isAllUppercaseName(name)) { name = Character.toLowerCase(name.charAt(0)) + name.substring(1); } return name; } public static boolean isAllUppercaseName(String name) { int uppercaseChars = 0; for (int i = 0; i < name.length(); i++) { if (Character.isLowerCase(name.charAt(i))) { return false; } if (Character.isUpperCase(name.charAt(i))) { uppercaseChars++; } } return uppercaseChars >= 3; } protected String getTestDirectoryName() { final String testName = getTestName(true); return testName.replaceAll("_.*", ""); } protected static void assertSameLinesWithFile(String filePath, String actualText) { String fileText; try { if (OVERWRITE_TESTDATA) { VfsTestUtil.overwriteTestData(filePath, actualText); System.out.println("File " + filePath + " created."); } fileText = FileUtil.loadFile(new File(filePath), CharsetToolkit.UTF8); } catch (FileNotFoundException e) { VfsTestUtil.overwriteTestData(filePath, actualText); throw new AssertionFailedError("No output text found. File " + filePath + " created."); } catch (IOException e) { throw new RuntimeException(e); } String expected = StringUtil.convertLineSeparators(fileText.trim()); String actual = StringUtil.convertLineSeparators(actualText.trim()); if (!Comparing.equal(expected, actual)) { throw new FileComparisonFailure(null, expected, actual, filePath); } } public static void clearFields(final Object test) throws IllegalAccessException { Class aClass = test.getClass(); while (aClass != null) { clearDeclaredFields(test, aClass); aClass = aClass.getSuperclass(); } } public static void clearDeclaredFields(Object test, Class aClass) throws IllegalAccessException { if (aClass == null) return; for (final Field field : aClass.getDeclaredFields()) { @NonNls final String name = field.getDeclaringClass().getName(); if (!name.startsWith("junit.framework.") && !name.startsWith("com.intellij.testFramework.")) { final int modifiers = field.getModifiers(); if ((modifiers & Modifier.FINAL) == 0 && (modifiers & Modifier.STATIC) == 0 && !field.getType().isPrimitive()) { field.setAccessible(true); field.set(test, null); } } } } @SuppressWarnings("deprecation") protected static void checkSettingsEqual( JDOMExternalizable expected, JDOMExternalizable settings, String message) throws Exception { if (expected == null || settings == null) return; Element oldS = new Element("temp"); expected.writeExternal(oldS); Element newS = new Element("temp"); settings.writeExternal(newS); String newString = JDOMUtil.writeElement(newS, "\n"); String oldString = JDOMUtil.writeElement(oldS, "\n"); Assert.assertEquals(message, oldString, newString); } public boolean isPerformanceTest() { String name = getName(); return name != null && name.contains("Performance") || getClass().getName().contains("Performance"); } public static void doPostponedFormatting(final Project project) { DocumentUtil.writeInRunUndoTransparentAction( new Runnable() { @Override public void run() { PsiDocumentManager.getInstance(project).commitAllDocuments(); PostprocessReformattingAspect.getInstance(project).doPostponedFormatting(); } }); } protected static void checkAllTimersAreDisposed() { try { Class<?> aClass = Class.forName("javax.swing.TimerQueue"); Method inst = aClass.getDeclaredMethod("sharedInstance"); inst.setAccessible(true); Object queue = inst.invoke(null); Field field = aClass.getDeclaredField("firstTimer"); field.setAccessible(true); Object firstTimer = field.get(queue); if (firstTimer != null) { try { fail("Not disposed Timer: " + firstTimer.toString() + "; queue:" + queue); } finally { field.set(queue, null); } } } catch (Throwable e) { // Ignore } } /** * Checks that code block throw corresponding exception. * * @param exceptionCase Block annotated with some exception type * @throws Throwable */ protected void assertException(final AbstractExceptionCase exceptionCase) throws Throwable { assertException(exceptionCase, null); } /** * Checks that code block throw corresponding exception with expected error msg. If expected error * message is null it will not be checked. * * @param exceptionCase Block annotated with some exception type * @param expectedErrorMsg expected error messge * @throws Throwable */ protected void assertException( final AbstractExceptionCase exceptionCase, @Nullable final String expectedErrorMsg) throws Throwable { assertExceptionOccurred(true, exceptionCase, expectedErrorMsg); } /** * Checks that code block doesn't throw corresponding exception. * * @param exceptionCase Block annotated with some exception type * @throws Throwable */ protected void assertNoException(final AbstractExceptionCase exceptionCase) throws Throwable { assertExceptionOccurred(false, exceptionCase, null); } protected void assertNoThrowable(final Runnable closure) { String throwableName = null; try { closure.run(); } catch (Throwable thr) { throwableName = thr.getClass().getName(); } assertNull(throwableName); } private static void assertExceptionOccurred( boolean shouldOccur, AbstractExceptionCase exceptionCase, String expectedErrorMsg) throws Throwable { boolean wasThrown = false; try { exceptionCase.tryClosure(); } catch (Throwable e) { if (shouldOccur) { wasThrown = true; final String errorMessage = exceptionCase.getAssertionErrorMessage(); assertEquals(errorMessage, exceptionCase.getExpectedExceptionClass(), e.getClass()); if (expectedErrorMsg != null) { assertEquals("Compare error messages", expectedErrorMsg, e.getMessage()); } } else if (exceptionCase.getExpectedExceptionClass().equals(e.getClass())) { wasThrown = true; System.out.println(""); e.printStackTrace(System.out); fail("Exception isn't expected here. Exception message: " + e.getMessage()); } else { throw e; } } finally { if (shouldOccur && !wasThrown) { fail(exceptionCase.getAssertionErrorMessage()); } } } protected boolean annotatedWith(@NotNull Class annotationClass) { Class<?> aClass = getClass(); String methodName = "test" + getTestName(false); boolean methodChecked = false; while (aClass != null && aClass != Object.class) { if (aClass.getAnnotation(annotationClass) != null) return true; if (!methodChecked) { try { Method method = aClass.getDeclaredMethod(methodName); if (method.getAnnotation(annotationClass) != null) return true; methodChecked = true; } catch (NoSuchMethodException ignored) { } } aClass = aClass.getSuperclass(); } return false; } protected String getHomePath() { return PathManager.getHomePath().replace(File.separatorChar, '/'); } protected static boolean isInHeadlessEnvironment() { return GraphicsEnvironment.isHeadless(); } public static void refreshRecursively(@NotNull VirtualFile file) { VfsUtilCore.visitChildrenRecursively( file, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile file) { file.getChildren(); return true; } }); file.refresh(false, true); } @NotNull public static Test filteredSuite(@RegExp String regexp, @NotNull Test test) { final Pattern pattern = Pattern.compile(regexp); final TestSuite testSuite = new TestSuite(); new Processor<Test>() { @Override public boolean process(Test test) { if (test instanceof TestSuite) { for (int i = 0, len = ((TestSuite) test).testCount(); i < len; i++) { process(((TestSuite) test).testAt(i)); } } else if (pattern.matcher(test.toString()).find()) { testSuite.addTest(test); } return false; } }.process(test); return testSuite; } }
/** Author: msk */ public class EditorsSplitters extends IdePanePanel implements UISettingsListener, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.EditorsSplitters"); private static final String PINNED = "pinned"; private static final String CURRENT_IN_TAB = "current-in-tab"; private static final Key<Object> DUMMY_KEY = Key.create("EditorsSplitters.dummy.key"); private EditorWindow myCurrentWindow; private final Set<EditorWindow> myWindows = new CopyOnWriteArraySet<EditorWindow>(); private final FileEditorManagerImpl myManager; private Element mySplittersElement; // temporarily used during initialization int myInsideChange; private final MyFocusWatcher myFocusWatcher; private final Alarm myIconUpdaterAlarm = new Alarm(); private final UIBuilder myUIBuilder = new UIBuilder(); EditorsSplitters( final FileEditorManagerImpl manager, DockManager dockManager, boolean createOwnDockableContainer) { super(new BorderLayout()); myManager = manager; myFocusWatcher = new MyFocusWatcher(); setFocusTraversalPolicy(new MyFocusTraversalPolicy()); clear(); if (createOwnDockableContainer) { DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), this, false); Disposer.register(manager.getProject(), dockable); dockManager.register(dockable); } KeymapManagerListener keymapListener = new KeymapManagerListener() { @Override public void activeKeymapChanged(Keymap keymap) { invalidate(); repaint(); } }; KeymapManager.getInstance().addKeymapManagerListener(keymapListener, this); } public FileEditorManagerImpl getManager() { return myManager; } public void clear() { for (EditorWindow window : myWindows) { window.dispose(); } removeAll(); myWindows.clear(); setCurrentWindow(null); repaint(); // revalidate doesn't repaint correctly after "Close All" } void startListeningFocus() { myFocusWatcher.install(this); } private void stopListeningFocus() { myFocusWatcher.deinstall(this); } @Override public void dispose() { myIconUpdaterAlarm.cancelAllRequests(); stopListeningFocus(); } @Nullable public VirtualFile getCurrentFile() { if (myCurrentWindow != null) { return myCurrentWindow.getSelectedFile(); } return null; } private boolean showEmptyText() { return myCurrentWindow == null || myCurrentWindow.getFiles().length == 0; } @Override protected void paintComponent(Graphics g) { if (showEmptyText()) { Graphics2D gg = IdeBackgroundUtil.withFrameBackground(g, this); super.paintComponent(gg); g.setColor(UIUtil.isUnderDarcula() ? UIUtil.getBorderColor() : new Color(0, 0, 0, 50)); g.drawLine(0, 0, getWidth(), 0); } } public void writeExternal(final Element element) { if (getComponentCount() != 0) { final Component comp = getComponent(0); LOG.assertTrue(comp instanceof JPanel); final JPanel panel = (JPanel) comp; if (panel.getComponentCount() != 0) { element.addContent(writePanel(panel)); } } } @SuppressWarnings("HardCodedStringLiteral") private Element writePanel(final JPanel panel) { final Component comp = panel.getComponent(0); if (comp instanceof Splitter) { final Splitter splitter = (Splitter) comp; final Element res = new Element("splitter"); res.setAttribute("split-orientation", splitter.getOrientation() ? "vertical" : "horizontal"); res.setAttribute("split-proportion", Float.toString(splitter.getProportion())); final Element first = new Element("split-first"); first.addContent(writePanel((JPanel) splitter.getFirstComponent())); final Element second = new Element("split-second"); second.addContent(writePanel((JPanel) splitter.getSecondComponent())); res.addContent(first); res.addContent(second); return res; } else if (comp instanceof JBTabs) { final Element res = new Element("leaf"); Integer limit = UIUtil.getClientProperty( ((JBTabs) comp).getComponent(), JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY); if (limit != null) { res.setAttribute(JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY.toString(), String.valueOf(limit)); } writeWindow(res, findWindowWith(comp)); return res; } else if (comp instanceof EditorWindow.TCompForTablessMode) { EditorWithProviderComposite composite = ((EditorWindow.TCompForTablessMode) comp).myEditor; Element res = new Element("leaf"); res.addContent(writeComposite(composite.getFile(), composite, false, composite)); return res; } else { LOG.error(comp != null ? comp.getClass().getName() : null); return null; } } private void writeWindow(@NotNull Element res, @Nullable EditorWindow window) { if (window != null) { EditorWithProviderComposite[] composites = window.getEditors(); for (int i = 0; i < composites.length; i++) { VirtualFile file = window.getFileAt(i); res.addContent( writeComposite( file, composites[i], window.isFilePinned(file), window.getSelectedEditor())); } } } @NotNull private Element writeComposite( VirtualFile file, EditorWithProviderComposite composite, boolean pinned, EditorWithProviderComposite selectedEditor) { Element fileElement = new Element("file"); fileElement.setAttribute("leaf-file-name", file.getName()); // TODO: all files composite.currentStateAsHistoryEntry().writeExternal(fileElement, getManager().getProject()); fileElement.setAttribute(PINNED, Boolean.toString(pinned)); fileElement.setAttribute(CURRENT_IN_TAB, Boolean.toString(composite.equals(selectedEditor))); return fileElement; } public void openFiles() { if (mySplittersElement != null) { initializeProgress(); final JPanel comp = myUIBuilder.process(mySplittersElement, getTopPanel()); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { if (comp != null) { removeAll(); add(comp, BorderLayout.CENTER); mySplittersElement = null; } // clear empty splitters for (EditorWindow window : getWindows()) { if (window.getEditors().length == 0) { for (EditorWindow sibling : window.findSiblings()) { sibling.unsplit(false); } } } } }); } } private static void initializeProgress() { ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.setText(IdeBundle.message("loading.editors")); } } public int getEditorsCount() { return mySplittersElement == null ? 0 : countFiles(mySplittersElement); } private double myProgressStep; public void setProgressStep(double step) { myProgressStep = step; } private void updateProgress() { ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.setFraction(indicator.getFraction() + myProgressStep); } } private static int countFiles(Element element) { Integer value = new ConfigTreeReader<Integer>() { @Override protected Integer processFiles( @NotNull List<Element> fileElements, @Nullable Integer context) { return fileElements.size(); } @Override protected Integer processSplitter( @NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable Integer context) { Integer first = process(firstChild, null); Integer second = process(secondChild, null); return (first == null ? 0 : first) + (second == null ? 0 : second); } }.process(element, null); return value == null ? 0 : value; } public void readExternal(final Element element) { mySplittersElement = element; } @NotNull public VirtualFile[] getOpenFiles() { final Set<VirtualFile> files = new ArrayListSet<VirtualFile>(); for (final EditorWindow myWindow : myWindows) { final EditorWithProviderComposite[] editors = myWindow.getEditors(); for (final EditorWithProviderComposite editor : editors) { VirtualFile file = editor.getFile(); // background thread may call this method when invalid file is being removed // do not return it here as it will quietly drop out soon if (file.isValid()) { files.add(file); } } } return VfsUtilCore.toVirtualFileArray(files); } @NotNull public VirtualFile[] getSelectedFiles() { final Set<VirtualFile> files = new ArrayListSet<VirtualFile>(); for (final EditorWindow window : myWindows) { final VirtualFile file = window.getSelectedFile(); if (file != null) { files.add(file); } } final VirtualFile[] virtualFiles = VfsUtilCore.toVirtualFileArray(files); final VirtualFile currentFile = getCurrentFile(); if (currentFile != null) { for (int i = 0; i != virtualFiles.length; ++i) { if (Comparing.equal(virtualFiles[i], currentFile)) { virtualFiles[i] = virtualFiles[0]; virtualFiles[0] = currentFile; break; } } } return virtualFiles; } @NotNull public FileEditor[] getSelectedEditors() { List<FileEditor> editors = new ArrayList<FileEditor>(); Set<EditorWindow> windows = new THashSet<EditorWindow>(myWindows); final EditorWindow currentWindow = getCurrentWindow(); if (currentWindow != null) { windows.add(currentWindow); } for (final EditorWindow window : windows) { final EditorWithProviderComposite composite = window.getSelectedEditor(); if (composite != null) { editors.add(composite.getSelectedEditor()); } } return editors.toArray(new FileEditor[editors.size()]); } public void updateFileIcon(@NotNull final VirtualFile file) { updateFileIconLater(file); } private void updateFileIconImmediately(final VirtualFile file) { final Collection<EditorWindow> windows = findWindows(file); for (EditorWindow window : windows) { window.updateFileIcon(file); } } private final Set<VirtualFile> myFilesToUpdateIconsFor = new HashSet<VirtualFile>(); private void updateFileIconLater(VirtualFile file) { myFilesToUpdateIconsFor.add(file); myIconUpdaterAlarm.cancelAllRequests(); myIconUpdaterAlarm.addRequest( () -> { if (myManager.getProject().isDisposed()) return; for (VirtualFile file1 : myFilesToUpdateIconsFor) { updateFileIconImmediately(file1); } myFilesToUpdateIconsFor.clear(); }, 200, ModalityState.stateForComponent(this)); } void updateFileColor(@NotNull final VirtualFile file) { final Collection<EditorWindow> windows = findWindows(file); for (final EditorWindow window : windows) { final int index = window.findEditorIndex(window.findFileComposite(file)); LOG.assertTrue(index != -1); window.setForegroundAt(index, getManager().getFileColor(file)); window.setWaveColor(index, getManager().isProblem(file) ? JBColor.red : null); } } public void trimToSize(final int editor_tab_limit) { for (final EditorWindow window : myWindows) { window.trimToSize(editor_tab_limit, null, true); } } public void setTabsPlacement(final int tabPlacement) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { windows[i].setTabsPlacement(tabPlacement); } } void setTabLayoutPolicy(int scrollTabLayout) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { windows[i].setTabLayoutPolicy(scrollTabLayout); } } void updateFileName(@Nullable final VirtualFile updatedFile) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { for (VirtualFile file : windows[i].getFiles()) { if (updatedFile == null || file.getName().equals(updatedFile.getName())) { windows[i].updateFileName(file); } } } Project project = myManager.getProject(); final IdeFrame frame = getFrame(project); if (frame != null) { VirtualFile file = getCurrentFile(); File ioFile = file == null ? null : new File(file.getPresentableUrl()); String fileTitle = null; if (file != null) { fileTitle = DumbService.isDumb(project) ? file.getName() : FrameTitleBuilder.getInstance().getFileTitle(project, file); } frame.setFileTitle(fileTitle, ioFile); } } protected IdeFrame getFrame(Project project) { final WindowManagerEx windowManagerEx = WindowManagerEx.getInstanceEx(); final IdeFrame frame = windowManagerEx.getFrame(project); LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || frame != null); return frame; } boolean isInsideChange() { return myInsideChange > 0; } private void setCurrentWindow(@Nullable final EditorWindow currentWindow) { if (currentWindow != null && !myWindows.contains(currentWindow)) { throw new IllegalArgumentException(currentWindow + " is not a member of this container"); } myCurrentWindow = currentWindow; } void updateFileBackgroundColor(@NotNull VirtualFile file) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { windows[i].updateFileBackgroundColor(file); } } int getSplitCount() { if (getComponentCount() > 0) { JPanel panel = (JPanel) getComponent(0); return getSplitCount(panel); } return 0; } private static int getSplitCount(JComponent component) { if (component.getComponentCount() > 0) { final JComponent firstChild = (JComponent) component.getComponent(0); if (firstChild instanceof Splitter) { final Splitter splitter = (Splitter) firstChild; return getSplitCount(splitter.getFirstComponent()) + getSplitCount(splitter.getSecondComponent()); } return 1; } return 0; } protected void afterFileClosed(VirtualFile file) {} protected void afterFileOpen(VirtualFile file) {} @Nullable JBTabs getTabsAt(RelativePoint point) { Point thisPoint = point.getPoint(this); Component c = SwingUtilities.getDeepestComponentAt(this, thisPoint.x, thisPoint.y); while (c != null) { if (c instanceof JBTabs) { return (JBTabs) c; } c = c.getParent(); } return null; } boolean isEmptyVisible() { EditorWindow[] windows = getWindows(); for (EditorWindow each : windows) { if (!each.isEmptyVisible()) { return false; } } return true; } @Nullable private VirtualFile findNextFile(final VirtualFile file) { final EditorWindow[] windows = getWindows(); // TODO: use current file as base for (int i = 0; i != windows.length; ++i) { final VirtualFile[] files = windows[i].getFiles(); for (final VirtualFile fileAt : files) { if (!Comparing.equal(fileAt, file)) { return fileAt; } } } return null; } void closeFile(VirtualFile file, boolean moveFocus) { final List<EditorWindow> windows = findWindows(file); if (!windows.isEmpty()) { final VirtualFile nextFile = findNextFile(file); for (final EditorWindow window : windows) { LOG.assertTrue(window.getSelectedEditor() != null); window.closeFile(file, false, moveFocus); if (window.getTabCount() == 0 && nextFile != null && myManager.getProject().isOpen()) { EditorWithProviderComposite newComposite = myManager.newEditorComposite(nextFile); window.setEditor(newComposite, moveFocus); // newComposite can be null } } // cleanup windows with no tabs for (final EditorWindow window : windows) { if (window.isDisposed()) { // call to window.unsplit() which might make its sibling disposed continue; } if (window.getTabCount() == 0) { window.unsplit(false); } } } } @Override public void uiSettingsChanged(UISettings source) { if (!myManager.getProject().isOpen()) return; for (VirtualFile file : getOpenFiles()) { updateFileBackgroundColor(file); updateFileIcon(file); updateFileColor(file); } } private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy { @Override public final Component getDefaultComponentImpl(final Container focusCycleRoot) { if (myCurrentWindow != null) { final EditorWithProviderComposite selectedEditor = myCurrentWindow.getSelectedEditor(); if (selectedEditor != null) { return IdeFocusTraversalPolicy.getPreferredFocusedComponent( selectedEditor.getComponent(), this); } } return IdeFocusTraversalPolicy.getPreferredFocusedComponent(EditorsSplitters.this, this); } } @Nullable public JPanel getTopPanel() { return getComponentCount() > 0 ? (JPanel) getComponent(0) : null; } public EditorWindow getCurrentWindow() { return myCurrentWindow; } public EditorWindow getOrCreateCurrentWindow(final VirtualFile file) { final List<EditorWindow> windows = findWindows(file); if (getCurrentWindow() == null) { final Iterator<EditorWindow> iterator = myWindows.iterator(); if (!windows.isEmpty()) { setCurrentWindow(windows.get(0), false); } else if (iterator.hasNext()) { setCurrentWindow(iterator.next(), false); } else { createCurrentWindow(); } } else if (!windows.isEmpty()) { if (!windows.contains(getCurrentWindow())) { setCurrentWindow(windows.get(0), false); } } return getCurrentWindow(); } void createCurrentWindow() { LOG.assertTrue(myCurrentWindow == null); setCurrentWindow(createEditorWindow()); add(myCurrentWindow.myPanel, BorderLayout.CENTER); } protected EditorWindow createEditorWindow() { return new EditorWindow(this); } /** * sets the window passed as a current ('focused') window among all splitters. All file openings * will be done inside this current window * * @param window a window to be set as current * @param requestFocus whether to request focus to the editor currently selected in this window */ void setCurrentWindow(@Nullable final EditorWindow window, final boolean requestFocus) { final EditorWithProviderComposite newEditor = window == null ? null : window.getSelectedEditor(); Runnable fireRunnable = () -> getManager().fireSelectionChanged(newEditor); setCurrentWindow(window); getManager().updateFileName(window == null ? null : window.getSelectedFile()); if (window != null) { final EditorWithProviderComposite selectedEditor = window.getSelectedEditor(); if (selectedEditor != null) { fireRunnable.run(); } if (requestFocus) { window.requestFocus(true); } } else { fireRunnable.run(); } } void addWindow(EditorWindow window) { myWindows.add(window); } void removeWindow(EditorWindow window) { myWindows.remove(window); if (myCurrentWindow == window) { myCurrentWindow = null; } } boolean containsWindow(EditorWindow window) { return myWindows.contains(window); } // --------------------------------------------------------- public EditorWithProviderComposite[] getEditorsComposites() { List<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); for (final EditorWindow myWindow : myWindows) { final EditorWithProviderComposite[] editors = myWindow.getEditors(); ContainerUtil.addAll(res, editors); } return res.toArray(new EditorWithProviderComposite[res.size()]); } // --------------------------------------------------------- @NotNull public List<EditorWithProviderComposite> findEditorComposites(@NotNull VirtualFile file) { List<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); for (final EditorWindow window : myWindows) { final EditorWithProviderComposite fileComposite = window.findFileComposite(file); if (fileComposite != null) { res.add(fileComposite); } } return res; } @NotNull private List<EditorWindow> findWindows(final VirtualFile file) { List<EditorWindow> res = new ArrayList<EditorWindow>(); for (final EditorWindow window : myWindows) { if (window.findFileComposite(file) != null) { res.add(window); } } return res; } @NotNull public EditorWindow[] getWindows() { return myWindows.toArray(new EditorWindow[myWindows.size()]); } @NotNull EditorWindow[] getOrderedWindows() { final List<EditorWindow> res = new ArrayList<EditorWindow>(); // Collector for windows in tree ordering: class Inner { private void collect(final JPanel panel) { final Component comp = panel.getComponent(0); if (comp instanceof Splitter) { final Splitter splitter = (Splitter) comp; collect((JPanel) splitter.getFirstComponent()); collect((JPanel) splitter.getSecondComponent()); } else if (comp instanceof JPanel || comp instanceof JBTabs) { final EditorWindow window = findWindowWith(comp); if (window != null) { res.add(window); } } } } // get root component and traverse splitters tree: if (getComponentCount() != 0) { final Component comp = getComponent(0); LOG.assertTrue(comp instanceof JPanel); final JPanel panel = (JPanel) comp; if (panel.getComponentCount() != 0) { new Inner().collect(panel); } } LOG.assertTrue(res.size() == myWindows.size()); return res.toArray(new EditorWindow[res.size()]); } @Nullable private EditorWindow findWindowWith(final Component component) { if (component != null) { for (final EditorWindow window : myWindows) { if (SwingUtilities.isDescendingFrom(component, window.myPanel)) { return window; } } } return null; } public boolean isFloating() { return false; } public boolean isPreview() { return false; } private final class MyFocusWatcher extends FocusWatcher { @Override protected void focusedComponentChanged(final Component component, final AWTEvent cause) { EditorWindow newWindow = null; if (component != null) { newWindow = findWindowWith(component); } else if (cause instanceof ContainerEvent && cause.getID() == ContainerEvent.COMPONENT_REMOVED) { // do not change current window in case of child removal as in JTable.removeEditor // otherwise Escape in a toolwindow will not focus editor with JTable content return; } setCurrentWindow(newWindow); setCurrentWindow(newWindow, false); } } private abstract static class ConfigTreeReader<T> { @Nullable public T process(@Nullable Element element, @Nullable T context) { if (element == null) { return null; } final Element splitterElement = element.getChild("splitter"); if (splitterElement != null) { final Element first = splitterElement.getChild("split-first"); final Element second = splitterElement.getChild("split-second"); return processSplitter(splitterElement, first, second, context); } final Element leaf = element.getChild("leaf"); if (leaf == null) { return null; } List<Element> fileElements = leaf.getChildren("file"); final List<Element> children = new ArrayList<Element>(fileElements.size()); // trim to EDITOR_TAB_LIMIT, ignoring CLOSE_NON_MODIFIED_FILES_FIRST policy int toRemove = fileElements.size() - UISettings.getInstance().EDITOR_TAB_LIMIT; for (Element fileElement : fileElements) { if (toRemove <= 0 || Boolean.valueOf(fileElement.getAttributeValue(PINNED)).booleanValue()) { children.add(fileElement); } else { toRemove--; } } return processFiles(children, context); } @Nullable protected abstract T processFiles(@NotNull List<Element> fileElements, @Nullable T context); @Nullable protected abstract T processSplitter( @NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context); } private class UIBuilder extends ConfigTreeReader<JPanel> { @Override protected JPanel processFiles(@NotNull List<Element> fileElements, final JPanel context) { final Ref<EditorWindow> windowRef = new Ref<EditorWindow>(); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { windowRef.set(context == null ? createEditorWindow() : findWindowWith(context)); } }); final EditorWindow window = windowRef.get(); LOG.assertTrue(window != null); VirtualFile focusedFile = null; for (int i = 0; i < fileElements.size(); i++) { final Element file = fileElements.get(i); if (i == 0) { EditorTabbedContainer tabbedPane = window.getTabbedPane(); if (tabbedPane != null) { try { int limit = Integer.parseInt( file.getParentElement() .getAttributeValue( JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY.toString(), String.valueOf(JBTabsImpl.DEFAULT_MAX_TAB_WIDTH))); UIUtil.putClientProperty( tabbedPane.getComponent(), JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY, limit); } catch (NumberFormatException e) { // ignore } } } try { final FileEditorManagerImpl fileEditorManager = getManager(); Element historyElement = file.getChild(HistoryEntry.TAG); final HistoryEntry entry = HistoryEntry.createLight(fileEditorManager.getProject(), historyElement); final VirtualFile virtualFile = entry.getFile(); if (virtualFile == null) throw new InvalidDataException("No file exists: " + entry.getFilePointer().getUrl()); Document document = ApplicationManager.getApplication() .runReadAction( new Computable<Document>() { @Override public Document compute() { return virtualFile.isValid() ? FileDocumentManager.getInstance().getDocument(virtualFile) : null; } }); final boolean isCurrentInTab = Boolean.valueOf(file.getAttributeValue(CURRENT_IN_TAB)).booleanValue(); Boolean pin = Boolean.valueOf(file.getAttributeValue(PINNED)); fileEditorManager.openFileImpl4( window, virtualFile, entry, isCurrentInTab, isCurrentInTab, pin, i); if (isCurrentInTab) { focusedFile = virtualFile; } if (document != null) { // This is just to make sure document reference is kept on stack till this point // so that document is available for folding state deserialization in HistoryEntry // constructor // and that document will be created only once during file opening document.putUserData(DUMMY_KEY, null); } updateProgress(); } catch (InvalidDataException e) { if (ApplicationManager.getApplication().isUnitTestMode()) { LOG.error(e); } } } if (focusedFile != null) { getManager().addSelectionRecord(focusedFile, window); } return window.myPanel; } @Override protected JPanel processSplitter( @NotNull Element splitterElement, Element firstChild, Element secondChild, final JPanel context) { if (context == null) { final boolean orientation = "vertical".equals(splitterElement.getAttributeValue("split-orientation")); final float proportion = Float.valueOf(splitterElement.getAttributeValue("split-proportion")).floatValue(); final JPanel firstComponent = process(firstChild, null); final JPanel secondComponent = process(secondChild, null); final Ref<JPanel> panelRef = new Ref<JPanel>(); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { JPanel panel = new JPanel(new BorderLayout()); panel.setOpaque(false); Splitter splitter = new OnePixelSplitter(orientation, proportion, 0.1f, 0.9f); panel.add(splitter, BorderLayout.CENTER); splitter.setFirstComponent(firstComponent); splitter.setSecondComponent(secondComponent); panelRef.set(panel); } }); return panelRef.get(); } final Ref<JPanel> firstComponent = new Ref<JPanel>(); final Ref<JPanel> secondComponent = new Ref<JPanel>(); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { if (context.getComponent(0) instanceof Splitter) { Splitter splitter = (Splitter) context.getComponent(0); firstComponent.set((JPanel) splitter.getFirstComponent()); secondComponent.set((JPanel) splitter.getSecondComponent()); } else { firstComponent.set(context); secondComponent.set(context); } } }); process(firstChild, firstComponent.get()); process(secondChild, secondComponent.get()); return context; } } }
public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingPass implements DumbAware { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.GeneralHighlightingPass"); static final String PRESENTABLE_NAME = DaemonBundle.message("pass.syntax"); private static final Key<Boolean> HAS_ERROR_ELEMENT = Key.create("HAS_ERROR_ELEMENT"); private final int myStartOffset; private final int myEndOffset; private final boolean myUpdateAll; private final ProperTextRange myPriorityRange; private final Editor myEditor; private final List<HighlightInfo> myHighlights = new ArrayList<HighlightInfo>(); protected volatile boolean myHasErrorElement; private volatile boolean myErrorFound; private static final Comparator<HighlightVisitor> VISITOR_ORDER_COMPARATOR = new Comparator<HighlightVisitor>() { @Override public int compare(final HighlightVisitor o1, final HighlightVisitor o2) { return o1.order() - o2.order(); } }; private Runnable myApplyCommand; private final EditorColorsScheme myGlobalScheme; private boolean myFailFastOnAcquireReadAction = true; public GeneralHighlightingPass( @NotNull Project project, @NotNull PsiFile file, @NotNull Document document, int startOffset, int endOffset, boolean updateAll) { this( project, file, document, startOffset, endOffset, updateAll, new ProperTextRange(0, document.getTextLength()), null); } public GeneralHighlightingPass( @NotNull Project project, @NotNull PsiFile file, @NotNull Document document, int startOffset, int endOffset, boolean updateAll, @NotNull ProperTextRange priorityRange, @Nullable Editor editor) { super(project, document, PRESENTABLE_NAME, file, true); myStartOffset = startOffset; myEndOffset = endOffset; myUpdateAll = updateAll; myPriorityRange = priorityRange; myEditor = editor; LOG.assertTrue(file.isValid()); setId(Pass.UPDATE_ALL); myHasErrorElement = !isWholeFileHighlighting() && Boolean.TRUE.equals(myFile.getUserData(HAS_ERROR_ELEMENT)); FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(myProject)).getFileStatusMap(); myErrorFound = !isWholeFileHighlighting() && fileStatusMap.wasErrorFound(myDocument); myApplyCommand = new Runnable() { @Override public void run() { ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset); MarkupModel model = DocumentMarkupModel.forDocument(myDocument, myProject, true); UpdateHighlightersUtil.cleanFileLevelHighlights(myProject, Pass.UPDATE_ALL, myFile); final EditorColorsScheme colorsScheme = getColorsScheme(); UpdateHighlightersUtil.setHighlightersInRange( myProject, myDocument, range, colorsScheme, myHighlights, (MarkupModelEx) model, Pass.UPDATE_ALL); } }; // initial guess to show correct progress in the traffic light icon setProgressLimit(document.getTextLength() / 2); // approx number of PSI elements = file length/2 myGlobalScheme = EditorColorsManager.getInstance().getGlobalScheme(); } private static final Key<AtomicInteger> HIGHLIGHT_VISITOR_INSTANCE_COUNT = new Key<AtomicInteger>("HIGHLIGHT_VISITOR_INSTANCE_COUNT"); @NotNull private HighlightVisitor[] getHighlightVisitors() { int oldCount = incVisitorUsageCount(1); HighlightVisitor[] highlightVisitors = createHighlightVisitors(); if (oldCount != 0) { HighlightVisitor[] clones = new HighlightVisitor[highlightVisitors.length]; for (int i = 0; i < highlightVisitors.length; i++) { HighlightVisitor highlightVisitor = highlightVisitors[i]; clones[i] = highlightVisitor.clone(); } highlightVisitors = clones; } return highlightVisitors; } protected HighlightVisitor[] createHighlightVisitors() { return Extensions.getExtensions(HighlightVisitor.EP_HIGHLIGHT_VISITOR, myProject); } // returns old value private int incVisitorUsageCount(int delta) { AtomicInteger count = myProject.getUserData(HIGHLIGHT_VISITOR_INSTANCE_COUNT); if (count == null) { count = ((UserDataHolderEx) myProject) .putUserDataIfAbsent(HIGHLIGHT_VISITOR_INSTANCE_COUNT, new AtomicInteger(0)); } int old = count.getAndAdd(delta); assert old + delta >= 0 : old + ";" + delta; return old; } @Override protected void collectInformationWithProgress(final ProgressIndicator progress) { final Set<HighlightInfo> gotHighlights = new THashSet<HighlightInfo>(100); final Set<HighlightInfo> outsideResult = new THashSet<HighlightInfo>(100); DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject); HighlightVisitor[] highlightVisitors = getHighlightVisitors(); final HighlightVisitor[] filteredVisitors = filterVisitors(highlightVisitors, myFile); final List<PsiElement> inside = new ArrayList<PsiElement>(); final List<PsiElement> outside = new ArrayList<PsiElement>(); try { Divider.divideInsideAndOutside( myFile, myStartOffset, myEndOffset, myPriorityRange, inside, outside, HighlightLevelUtil.AnalysisLevel.HIGHLIGHT, false); setProgressLimit((long) (inside.size() + outside.size())); final boolean forceHighlightParents = forceHighlightParents(); if (!isDumbMode()) { highlightTodos( myFile, myDocument.getCharsSequence(), myStartOffset, myEndOffset, progress, myPriorityRange, gotHighlights, outsideResult); } collectHighlights( inside, new Runnable() { @Override public void run() { // all infos for the "injected fragment for the host which is inside" are indeed // inside // but some of the infos for the "injected fragment for the host which is outside" can // be still inside Set<HighlightInfo> injectedResult = new THashSet<HighlightInfo>(); final Set<PsiFile> injected = new THashSet<PsiFile>(); getInjectedPsiFiles(inside, outside, progress, injected); if (!addInjectedPsiHighlights( injected, progress, Collections.synchronizedSet(injectedResult))) throw new ProcessCanceledException(); final List<HighlightInfo> injectionsOutside = new ArrayList<HighlightInfo>(gotHighlights.size()); Set<HighlightInfo> result; synchronized (injectedResult) { // sync here because all writes happened in another thread result = injectedResult; } for (HighlightInfo info : result) { if (myPriorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) { gotHighlights.add(info); } else { // nonconditionally apply injected results regardless whether they are in // myStartOffset,myEndOffset injectionsOutside.add(info); } } if (outsideResult.isEmpty() && injectionsOutside.isEmpty()) { return; // apply only result (by default apply command) and only within inside } final ProperTextRange priorityIntersection = myPriorityRange.intersection(new TextRange(myStartOffset, myEndOffset)); if ((!inside.isEmpty() || !gotHighlights.isEmpty()) && priorityIntersection != null) { // do not apply when there were no elements to highlight // clear infos found in visible area to avoid applying them twice final List<HighlightInfo> toApplyInside = new ArrayList<HighlightInfo>(gotHighlights); myHighlights.addAll(toApplyInside); gotHighlights.clear(); gotHighlights.addAll(outsideResult); final long modificationStamp = myDocument.getModificationStamp(); UIUtil.invokeLaterIfNeeded( new Runnable() { @Override public void run() { if (myProject.isDisposed() || modificationStamp != myDocument.getModificationStamp()) return; MarkupModel markupModel = DocumentMarkupModel.forDocument(myDocument, myProject, true); UpdateHighlightersUtil.setHighlightersInRange( myProject, myDocument, priorityIntersection, getColorsScheme(), toApplyInside, (MarkupModelEx) markupModel, Pass.UPDATE_ALL); if (myEditor != null) { new ShowAutoImportPass(myProject, myFile, myEditor) .applyInformationToEditor(); } } }); } myApplyCommand = new Runnable() { @Override public void run() { ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset); List<HighlightInfo> toApply = new ArrayList<HighlightInfo>(); for (HighlightInfo info : gotHighlights) { if (!range.containsRange(info.getStartOffset(), info.getEndOffset())) continue; if (!myPriorityRange.containsRange( info.getStartOffset(), info.getEndOffset())) { toApply.add(info); } } toApply.addAll(injectionsOutside); UpdateHighlightersUtil.setHighlightersOutsideRange( myProject, myDocument, toApply, getColorsScheme(), myStartOffset, myEndOffset, myPriorityRange, Pass.UPDATE_ALL); } }; } }, outside, progress, filteredVisitors, gotHighlights, forceHighlightParents); if (myUpdateAll) { ((DaemonCodeAnalyzerImpl) daemonCodeAnalyzer) .getFileStatusMap() .setErrorFoundFlag(myDocument, myErrorFound); } } finally { incVisitorUsageCount(-1); } myHighlights.addAll(gotHighlights); } private void getInjectedPsiFiles( @NotNull final List<PsiElement> elements1, @NotNull final List<PsiElement> elements2, @NotNull final ProgressIndicator progress, @NotNull final Set<PsiFile> outInjected) { List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(myFile); Collection<PsiElement> hosts = new THashSet<PsiElement>(elements1.size() + elements2.size() + injected.size()); // rehighlight all injected PSI regardless the range, // since change in one place can lead to invalidation of injected PSI in (completely) other // place. for (DocumentWindow documentRange : injected) { progress.checkCanceled(); if (!documentRange.isValid()) continue; PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(documentRange); if (file == null) continue; PsiElement context = file.getContext(); if (context != null && context.isValid() && !file.getProject().isDisposed() && (myUpdateAll || new ProperTextRange(myStartOffset, myEndOffset) .intersects(context.getTextRange()))) { hosts.add(context); } } hosts.addAll(elements1); hosts.addAll(elements2); final PsiLanguageInjectionHost.InjectedPsiVisitor visitor = new PsiLanguageInjectionHost.InjectedPsiVisitor() { @Override public void visit( @NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) { synchronized (outInjected) { outInjected.add(injectedPsi); } } }; if (!JobUtil.invokeConcurrentlyUnderProgress( new ArrayList<PsiElement>(hosts), progress, false, new Processor<PsiElement>() { @Override public boolean process(PsiElement element) { progress.checkCanceled(); InjectedLanguageUtil.enumerate(element, myFile, false, visitor); return true; } })) throw new ProcessCanceledException(); } // returns false if canceled private boolean addInjectedPsiHighlights( @NotNull final Set<PsiFile> injectedFiles, @NotNull final ProgressIndicator progress, @NotNull final Collection<HighlightInfo> outInfos) { if (injectedFiles.isEmpty()) return true; final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(myProject); final TextAttributes injectedAttributes = myGlobalScheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT); return JobUtil.invokeConcurrentlyUnderProgress( new ArrayList<PsiFile>(injectedFiles), progress, myFailFastOnAcquireReadAction, new Processor<PsiFile>() { @Override public boolean process(final PsiFile injectedPsi) { DocumentWindow documentWindow = (DocumentWindow) PsiDocumentManager.getInstance(myProject).getCachedDocument(injectedPsi); if (documentWindow == null) return true; Place places = InjectedLanguageUtil.getShreds(injectedPsi); for (PsiLanguageInjectionHost.Shred place : places) { TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset()); if (textRange.isEmpty()) continue; String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText(); HighlightInfo info = HighlightInfo.createHighlightInfo( HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, textRange, null, desc, injectedAttributes); info.fromInjection = true; outInfos.add(info); } HighlightInfoHolder holder = createInfoHolder(injectedPsi); runHighlightVisitorsForInjected(injectedPsi, holder, progress); for (int i = 0; i < holder.size(); i++) { HighlightInfo info = holder.get(i); final int startOffset = documentWindow.injectedToHost(info.startOffset); final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset); addPatchedInfos( info, injectedPsi, documentWindow, injectedLanguageManager, fixedTextRange, outInfos); } holder.clear(); highlightInjectedSyntax(injectedPsi, holder); for (int i = 0; i < holder.size(); i++) { HighlightInfo info = holder.get(i); final int startOffset = info.startOffset; final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset); if (fixedTextRange == null) { info.fromInjection = true; outInfos.add(info); } else { HighlightInfo patched = new HighlightInfo( info.forcedTextAttributes, info.forcedTextAttributesKey, info.type, fixedTextRange.getStartOffset(), fixedTextRange.getEndOffset(), info.description, info.toolTip, info.type.getSeverity(null), info.isAfterEndOfLine, null, false); patched.fromInjection = true; outInfos.add(patched); } } if (!isDumbMode()) { List<HighlightInfo> todos = new ArrayList<HighlightInfo>(); highlightTodos( injectedPsi, injectedPsi.getText(), 0, injectedPsi.getTextLength(), progress, myPriorityRange, todos, todos); for (HighlightInfo info : todos) { addPatchedInfos( info, injectedPsi, documentWindow, injectedLanguageManager, null, outInfos); } } return true; } }); } private static TextRange getFixedTextRange( @NotNull DocumentWindow documentWindow, int startOffset) { final TextRange fixedTextRange; TextRange textRange = documentWindow.getHostRange(startOffset); if (textRange == null) { // todo[cdr] check this fix. prefix/suffix code annotation case textRange = findNearestTextRange(documentWindow, startOffset); final boolean isBefore = startOffset < textRange.getStartOffset(); fixedTextRange = new ProperTextRange( isBefore ? textRange.getStartOffset() - 1 : textRange.getEndOffset(), isBefore ? textRange.getStartOffset() : textRange.getEndOffset() + 1); } else { fixedTextRange = null; } return fixedTextRange; } private static void addPatchedInfos( @NotNull HighlightInfo info, @NotNull PsiFile injectedPsi, @NotNull DocumentWindow documentWindow, @NotNull InjectedLanguageManager injectedLanguageManager, @Nullable TextRange fixedTextRange, @NotNull Collection<HighlightInfo> out) { ProperTextRange textRange = new ProperTextRange(info.startOffset, info.endOffset); List<TextRange> editables = injectedLanguageManager.intersectWithAllEditableFragments(injectedPsi, textRange); for (TextRange editable : editables) { TextRange hostRange = fixedTextRange == null ? documentWindow.injectedToHost(editable) : fixedTextRange; boolean isAfterEndOfLine = info.isAfterEndOfLine; if (isAfterEndOfLine) { // convert injected afterEndOfLine to either host' afterEndOfLine or not-afterEndOfLine // highlight of the injected fragment boundary int hostEndOffset = hostRange.getEndOffset(); int lineNumber = documentWindow.getDelegate().getLineNumber(hostEndOffset); int hostLineEndOffset = documentWindow.getDelegate().getLineEndOffset(lineNumber); if (hostEndOffset < hostLineEndOffset) { // convert to non-afterEndOfLine isAfterEndOfLine = false; hostRange = new ProperTextRange(hostRange.getStartOffset(), hostEndOffset + 1); } } HighlightInfo patched = new HighlightInfo( info.forcedTextAttributes, info.forcedTextAttributesKey, info.type, hostRange.getStartOffset(), hostRange.getEndOffset(), info.description, info.toolTip, info.type.getSeverity(null), isAfterEndOfLine, null, false); patched.setHint(info.hasHint()); patched.setGutterIconRenderer(info.getGutterIconRenderer()); if (info.quickFixActionRanges != null) { for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) { TextRange quickfixTextRange = pair.getSecond(); List<TextRange> editableQF = injectedLanguageManager.intersectWithAllEditableFragments( injectedPsi, quickfixTextRange); for (TextRange editableRange : editableQF) { HighlightInfo.IntentionActionDescriptor descriptor = pair.getFirst(); if (patched.quickFixActionRanges == null) patched.quickFixActionRanges = new ArrayList<Pair<HighlightInfo.IntentionActionDescriptor, TextRange>>(); TextRange hostEditableRange = documentWindow.injectedToHost(editableRange); patched.quickFixActionRanges.add(Pair.create(descriptor, hostEditableRange)); } } } patched.fromInjection = true; out.add(patched); } } // finds the first nearest text range private static TextRange findNearestTextRange( final DocumentWindow documentWindow, final int startOffset) { TextRange textRange = null; for (RangeMarker marker : documentWindow.getHostRanges()) { TextRange curRange = ProperTextRange.create(marker); if (curRange.getStartOffset() > startOffset && textRange != null) break; textRange = curRange; } assert textRange != null; return textRange; } private void runHighlightVisitorsForInjected( @NotNull PsiFile injectedPsi, @NotNull final HighlightInfoHolder holder, @NotNull final ProgressIndicator progress) { HighlightVisitor[] visitors = getHighlightVisitors(); try { HighlightVisitor[] filtered = filterVisitors(visitors, injectedPsi); final List<PsiElement> elements = CollectHighlightsUtil.getElementsInRange(injectedPsi, 0, injectedPsi.getTextLength()); for (final HighlightVisitor visitor : filtered) { visitor.analyze( injectedPsi, true, holder, new Runnable() { @Override public void run() { for (PsiElement element : elements) { progress.checkCanceled(); visitor.visit(element); } } }); } } finally { incVisitorUsageCount(-1); } } private void highlightInjectedSyntax(final PsiFile injectedPsi, HighlightInfoHolder holder) { List<Trinity<IElementType, PsiLanguageInjectionHost, TextRange>> tokens = InjectedLanguageUtil.getHighlightTokens(injectedPsi); if (tokens == null) return; final Language injectedLanguage = injectedPsi.getLanguage(); Project project = injectedPsi.getProject(); SyntaxHighlighter syntaxHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter( injectedLanguage, project, injectedPsi.getVirtualFile()); final TextAttributes defaultAttrs = myGlobalScheme.getAttributes(HighlighterColors.TEXT); for (Trinity<IElementType, PsiLanguageInjectionHost, TextRange> token : tokens) { ProgressManager.checkCanceled(); IElementType tokenType = token.getFirst(); PsiLanguageInjectionHost injectionHost = token.getSecond(); TextRange textRange = token.getThird(); TextAttributesKey[] keys = syntaxHighlighter.getTokenHighlights(tokenType); if (textRange.getLength() == 0) continue; TextRange annRange = textRange.shiftRight(injectionHost.getTextRange().getStartOffset()); // force attribute colors to override host' ones TextAttributes attributes = null; for (TextAttributesKey key : keys) { TextAttributes attrs2 = myGlobalScheme.getAttributes(key); if (attrs2 != null) { attributes = attributes == null ? attrs2 : TextAttributes.merge(attributes, attrs2); } } TextAttributes forcedAttributes; if (attributes == null || attributes.isEmpty() || attributes.equals(defaultAttrs)) { forcedAttributes = TextAttributes.ERASE_MARKER; } else { Color back = attributes.getBackgroundColor() == null ? myGlobalScheme.getDefaultBackground() : attributes.getBackgroundColor(); Color fore = attributes.getForegroundColor() == null ? myGlobalScheme.getDefaultForeground() : attributes.getForegroundColor(); forcedAttributes = new TextAttributes( fore, back, attributes.getEffectColor(), attributes.getEffectType(), attributes.getFontType()); } HighlightInfo info = HighlightInfo.createHighlightInfo( HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, annRange, null, null, forcedAttributes); holder.add(info); } } private boolean isWholeFileHighlighting() { return myUpdateAll && myStartOffset == 0 && myEndOffset == myDocument.getTextLength(); } @Override protected void applyInformationWithProgress() { myFile.putUserData(HAS_ERROR_ELEMENT, myHasErrorElement); myApplyCommand.run(); if (myUpdateAll) { reportErrorsToWolf(); } } @Override @NotNull public List<HighlightInfo> getInfos() { return new ArrayList<HighlightInfo>(myHighlights); } private void collectHighlights( @NotNull final List<PsiElement> elements1, @NotNull final Runnable after1, @NotNull final List<PsiElement> elements2, @NotNull final ProgressIndicator progress, @NotNull final HighlightVisitor[] visitors, @NotNull final Set<HighlightInfo> gotHighlights, final boolean forceHighlightParents) { final Set<PsiElement> skipParentsSet = new THashSet<PsiElement>(); // TODO - add color scheme to holder final HighlightInfoHolder holder = createInfoHolder(myFile); final int chunkSize = Math.max(1, (elements1.size() + elements2.size()) / 100); // one percent precision is enough final Runnable action = new Runnable() { @Override public void run() { //noinspection unchecked boolean failed = false; for (List<PsiElement> elements : new List[] {elements1, elements2}) { int nextLimit = chunkSize; for (int i = 0; i < elements.size(); i++) { PsiElement element = elements.get(i); progress.checkCanceled(); if (element != myFile && !skipParentsSet.isEmpty() && element.getFirstChild() != null && skipParentsSet.contains(element)) { skipParentsSet.add(element.getParent()); continue; } if (element instanceof PsiErrorElement) { myHasErrorElement = true; } holder.clear(); for (final HighlightVisitor visitor : visitors) { try { visitor.visit(element); } catch (ProcessCanceledException e) { throw e; } catch (IndexNotReadyException e) { throw e; } catch (WolfTheProblemSolverImpl.HaveGotErrorException e) { throw e; } catch (Exception e) { if (!failed) { LOG.error(e); } failed = true; } } if (i == nextLimit) { advanceProgress(chunkSize); nextLimit = i + chunkSize; } //noinspection ForLoopReplaceableByForEach for (int j = 0; j < holder.size(); j++) { final HighlightInfo info = holder.get(j); assert info != null; // have to filter out already obtained highlights if (!gotHighlights.add(info)) continue; boolean isError = info.getSeverity() == HighlightSeverity.ERROR; if (isError) { if (!forceHighlightParents) { skipParentsSet.add(element.getParent()); } myErrorFound = true; } myTransferToEDTQueue.offer(Pair.create(info, progress)); } } advanceProgress(elements.size() - (nextLimit - chunkSize)); if (elements == elements1) after1.run(); } } }; analyzeByVisitors(progress, visitors, holder, 0, action); } // private void collectHighlights(@NotNull final List<PsiElement> elements1, // @NotNull final Runnable after1, // @NotNull final List<PsiElement> elements2, // @NotNull final ProgressIndicator progress, // @NotNull final HighlightVisitor[] visitors, // @NotNull final Set<HighlightInfo> gotHighlights, // final boolean forceHighlightParents) { // final HighlightInfoHolder holder = createInfoHolder(myFile); // // final int chunkSize = Math.max(1, (elements1.size()+elements2.size()) / 100); // one percent // precision is enough // // final Runnable action = new Runnable() { // public void run() { // //noinspection unchecked // boolean failed = false; // PsiNodeTask task = createPsiTask(myFile); // JobSchedulerImpl.submitTask(task); // // for (List<PsiElement> elements : new List[]{elements1, elements2}) { // int nextLimit = chunkSize; // for (int i = 0; i < elements.size(); i++) { // PsiElement element = elements.get(i); // progress.checkCanceled(); // // if (element != myFile && !skipParentsSet.isEmpty() && element.getFirstChild() != null // && skipParentsSet.contains(element)) { // skipParentsSet.add(element.getParent()); // continue; // } // // if (element instanceof PsiErrorElement) { // myHasErrorElement = true; // } // kjlhkjh // if (i == nextLimit) { // advanceProgress(chunkSize); // nextLimit = i + chunkSize; // } // // } // advanceProgress(elements.size() - (nextLimit-chunkSize)); // if (elements == elements1) after1.run(); // } // } // // private PsiNodeTask createPsiTask(@NotNull PsiElement root) { // return new PsiNodeTask(root) { // @Override // public void onEnter(@NotNull PsiElement element) { // if (element instanceof PsiErrorElement) { // myHasErrorElement = true; // } // super.onEnter(element); // } // // @Override // protected PsiNodeTask forkNode(@NotNull PsiElement child) { // return createPsiTask(child); // } // // @Override // protected boolean highlight(PsiElement element) { // holder.clear(); // // for (final HighlightVisitor visitor : visitors) { // try { // visitor.visit(element); // } // catch (ProcessCanceledException e) { // throw e; // } // catch (IndexNotReadyException e) { // throw e; // } // catch (WolfTheProblemSolverImpl.HaveGotErrorException e) { // throw e; // } // catch (Exception e) { // LOG.error(e); // } // } // // //noinspection ForLoopReplaceableByForEach // for (int j = 0; j < holder.size(); j++) { // final HighlightInfo info = holder.get(j); // assert info != null; // // have to filter out already obtained highlights // if (!gotHighlights.add(info)) continue; // boolean isError = info.getSeverity() == HighlightSeverity.ERROR; // if (isError) { // if (!forceHighlightParents) { // skipParentsSet.add(element.getParent()); // } // myErrorFound = true; // } // myTransferToEDTQueue.offer(Pair.create(info, progress)); // } // return true; // } // }; // } // }; // // analyzeByVisitors(progress, visitors, holder, 0, action); // } private final Map<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>(); private final TransferToEDTQueue<Pair<HighlightInfo, ProgressIndicator>> myTransferToEDTQueue = new TransferToEDTQueue<Pair<HighlightInfo, ProgressIndicator>>( "Apply highlighting results", new Processor<Pair<HighlightInfo, ProgressIndicator>>() { @Override public boolean process(Pair<HighlightInfo, ProgressIndicator> pair) { ApplicationManager.getApplication().assertIsDispatchThread(); ProgressIndicator indicator = pair.getSecond(); if (indicator.isCanceled()) { return false; } HighlightInfo info = pair.getFirst(); final EditorColorsScheme colorsScheme = getColorsScheme(); UpdateHighlightersUtil.addHighlighterToEditorIncrementally( myProject, myDocument, myFile, myStartOffset, myEndOffset, info, colorsScheme, Pass.UPDATE_ALL, ranges2markersCache); return true; } }, myProject.getDisposed(), 200); private void analyzeByVisitors( @NotNull final ProgressIndicator progress, @NotNull final HighlightVisitor[] visitors, @NotNull final HighlightInfoHolder holder, final int i, @NotNull final Runnable action) { if (i == visitors.length) { action.run(); } else { if (!visitors[i].analyze( myFile, myUpdateAll, holder, new Runnable() { @Override public void run() { analyzeByVisitors(progress, visitors, holder, i + 1, action); } })) { cancelAndRestartDaemonLater(progress, myProject, this); } } } private static HighlightVisitor[] filterVisitors( HighlightVisitor[] highlightVisitors, final PsiFile file) { final List<HighlightVisitor> visitors = new ArrayList<HighlightVisitor>(highlightVisitors.length); List<HighlightVisitor> list = Arrays.asList(highlightVisitors); for (HighlightVisitor visitor : DumbService.getInstance(file.getProject()).filterByDumbAwareness(list)) { if (visitor.suitableForFile(file)) visitors.add(visitor); } LOG.assertTrue(!visitors.isEmpty(), list); HighlightVisitor[] visitorArray = visitors.toArray(new HighlightVisitor[visitors.size()]); Arrays.sort(visitorArray, VISITOR_ORDER_COMPARATOR); return visitorArray; } static Void cancelAndRestartDaemonLater( ProgressIndicator progress, final Project project, TextEditorHighlightingPass pass) { PassExecutorService.log(progress, pass, "Cancel and restart"); progress.cancel(); ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { LOG.error(e); } DaemonCodeAnalyzer.getInstance(project).restart(); } }, project.getDisposed()); throw new ProcessCanceledException(); } private boolean forceHighlightParents() { boolean forceHighlightParents = false; for (HighlightRangeExtension extension : Extensions.getExtensions(HighlightRangeExtension.EP_NAME)) { if (extension.isForceHighlightParents(myFile)) { forceHighlightParents = true; break; } } return forceHighlightParents; } protected HighlightInfoHolder createInfoHolder(final PsiFile file) { final HighlightInfoFilter[] filters = ApplicationManager.getApplication().getExtensions(HighlightInfoFilter.EXTENSION_POINT_NAME); return new HighlightInfoHolder(file, getColorsScheme(), filters); } private static void highlightTodos( @NotNull PsiFile file, @NotNull CharSequence text, int startOffset, int endOffset, @NotNull ProgressIndicator progress, @NotNull ProperTextRange priorityRange, @NotNull Collection<HighlightInfo> result, @NotNull Collection<HighlightInfo> outsideResult) { PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(file.getProject()); TodoItem[] todoItems = helper.findTodoItems(file, startOffset, endOffset); if (todoItems.length == 0) return; for (TodoItem todoItem : todoItems) { progress.checkCanceled(); TextRange range = todoItem.getTextRange(); String description = text.subSequence(range.getStartOffset(), range.getEndOffset()).toString(); TextAttributes attributes = todoItem.getPattern().getAttributes().getTextAttributes(); HighlightInfo info = HighlightInfo.createHighlightInfo( HighlightInfoType.TODO, range, description, description, attributes); assert info != null; if (priorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) { result.add(info); } else { outsideResult.add(info); } } } private void reportErrorsToWolf() { if (!myFile.getViewProvider().isPhysical()) return; // e.g. errors in evaluate expression Project project = myFile.getProject(); if (!PsiManager.getInstance(project).isInProject(myFile)) return; // do not report problems in libraries VirtualFile file = myFile.getVirtualFile(); if (file == null) return; List<Problem> problems = convertToProblems(getInfos(), file, myHasErrorElement); WolfTheProblemSolver wolf = WolfTheProblemSolver.getInstance(project); boolean hasErrors = DaemonCodeAnalyzerImpl.hasErrors(project, getDocument()); if (!hasErrors || isWholeFileHighlighting()) { wolf.reportProblems(file, problems); } else { wolf.weHaveGotProblems(file, problems); } } @Override public double getProgress() { // do not show progress of visible highlighters update return myUpdateAll ? super.getProgress() : -1; } private static List<Problem> convertToProblems( final Collection<HighlightInfo> infos, final VirtualFile file, final boolean hasErrorElement) { List<Problem> problems = new SmartList<Problem>(); for (HighlightInfo info : infos) { if (info.getSeverity() == HighlightSeverity.ERROR) { Problem problem = new ProblemImpl(file, info, hasErrorElement); problems.add(problem); } } return problems; } @Override public String toString() { return super.toString() + " updateAll=" + myUpdateAll + " range=(" + myStartOffset + "," + myEndOffset + ")"; } public void setFailFastOnAcquireReadAction(boolean failFastOnAcquireReadAction) { myFailFastOnAcquireReadAction = failFastOnAcquireReadAction; } }
public class StructureViewComponent extends SimpleToolWindowPanel implements TreeActionsOwner, DataProvider, StructureView.Scrollable { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.structureView.newStructureView.StructureViewComponent"); @NonNls private static final String ourHelpID = "viewingStructure.fileStructureView"; private StructureTreeBuilder myAbstractTreeBuilder; private FileEditor myFileEditor; private final TreeModelWrapper myTreeModelWrapper; private StructureViewState myStructureViewState; private boolean myAutoscrollFeedback; private final Alarm myAutoscrollAlarm = new Alarm(); private final CopyPasteDelegator myCopyPasteDelegator; private final MyAutoScrollToSourceHandler myAutoScrollToSourceHandler; private final AutoScrollFromSourceHandler myAutoScrollFromSourceHandler; private static final Key<StructureViewState> STRUCTURE_VIEW_STATE_KEY = Key.create("STRUCTURE_VIEW_STATE"); private final Project myProject; private final StructureViewModel myTreeModel; private static int ourSettingsModificationCount; private Tree myTree; public StructureViewComponent( FileEditor editor, StructureViewModel structureViewModel, Project project) { this(editor, structureViewModel, project, true); } public StructureViewComponent( final FileEditor editor, final StructureViewModel structureViewModel, final Project project, final boolean showRootNode) { super(true, true); myProject = project; myFileEditor = editor; myTreeModel = structureViewModel; myTreeModelWrapper = new TreeModelWrapper(myTreeModel, this); SmartTreeStructure treeStructure = new SmartTreeStructure(project, myTreeModelWrapper) { public void rebuildTree() { if (!isDisposed()) { super.rebuildTree(); } } public boolean isToBuildChildrenInBackground(final Object element) { return getRootElement() == element; } protected TreeElementWrapper createTree() { return new StructureViewTreeElementWrapper(myProject, myModel.getRoot(), myModel); } @Override public String toString() { return "structure view tree structure(model=" + myTreeModel + ")"; } }; final DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode(treeStructure.getRootElement())); myTree = new Tree(model); myTree.setRootVisible(showRootNode); myTree.setShowsRootHandles(true); myAbstractTreeBuilder = new StructureTreeBuilder( project, myTree, (DefaultTreeModel) myTree.getModel(), treeStructure, myTreeModelWrapper) { @Override protected boolean validateNode(Object child) { return isValid(child); } }; Disposer.register(this, myAbstractTreeBuilder); Disposer.register( myAbstractTreeBuilder, new Disposable() { public void dispose() { storeState(); } }); setContent(ScrollPaneFactory.createScrollPane(myAbstractTreeBuilder.getTree())); myAbstractTreeBuilder.getTree().setCellRenderer(new NodeRenderer()); myAutoScrollToSourceHandler = new MyAutoScrollToSourceHandler(); myAutoScrollFromSourceHandler = new MyAutoScrollFromSourceHandler(myProject, this); JComponent toolbarComponent = ActionManager.getInstance() .createActionToolbar(ActionPlaces.STRUCTURE_VIEW_TOOLBAR, createActionGroup(), true) .getComponent(); setToolbar(toolbarComponent); installTree(); myCopyPasteDelegator = new CopyPasteDelegator(myProject, getTree()) { @NotNull protected PsiElement[] getSelectedElements() { return getSelectedPsiElements(); } }; } private void installTree() { getTree().getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); myAutoScrollToSourceHandler.install(getTree()); myAutoScrollFromSourceHandler.install(); TreeUtil.installActions(getTree()); new TreeSpeedSearch(getTree()); addTreeKeyListener(); addTreeMouseListeners(); restoreState(); } private PsiElement[] getSelectedPsiElements() { return filterPsiElements(getSelectedElements()); } @NotNull private static PsiElement[] filterPsiElements(Object[] selectedElements) { if (selectedElements == null) { return PsiElement.EMPTY_ARRAY; } ArrayList<PsiElement> psiElements = new ArrayList<PsiElement>(); for (Object selectedElement : selectedElements) { if (selectedElement instanceof PsiElement) { psiElements.add((PsiElement) selectedElement); } } return PsiUtilBase.toPsiElementArray(psiElements); } private Object[] getSelectedElements() { final JTree tree = getTree(); return tree != null ? convertPathsToValues(tree.getSelectionPaths()) : ArrayUtil.EMPTY_OBJECT_ARRAY; } @Nullable private Object[] getSelectedTreeElements() { final JTree tree = getTree(); return tree != null ? convertPathsToTreeElements(tree.getSelectionPaths()) : null; } private static Object[] convertPathsToValues(TreePath[] selectionPaths) { if (selectionPaths != null) { List<Object> result = new ArrayList<Object>(); for (TreePath selectionPath : selectionPaths) { final Object userObject = ((DefaultMutableTreeNode) selectionPath.getLastPathComponent()).getUserObject(); if (userObject instanceof AbstractTreeNode) { Object value = ((AbstractTreeNode) userObject).getValue(); if (value instanceof StructureViewTreeElement) { value = ((StructureViewTreeElement) value).getValue(); } result.add(value); } } return ArrayUtil.toObjectArray(result); } else { return null; } } @Nullable private static Object[] convertPathsToTreeElements(TreePath[] selectionPaths) { if (selectionPaths != null) { Object[] result = new Object[selectionPaths.length]; for (int i = 0; i < selectionPaths.length; i++) { Object userObject = ((DefaultMutableTreeNode) selectionPaths[i].getLastPathComponent()).getUserObject(); if (!(userObject instanceof AbstractTreeNode)) return null; result[i] = ((AbstractTreeNode) userObject).getValue(); } return result; } else { return null; } } private void addTreeMouseListeners() { EditSourceOnDoubleClickHandler.install(getTree()); CustomizationUtil.installPopupHandler( getTree(), IdeActions.GROUP_STRUCTURE_VIEW_POPUP, ActionPlaces.STRUCTURE_VIEW_POPUP); } private void addTreeKeyListener() { getTree() .addKeyListener( new KeyAdapter() { public void keyPressed(KeyEvent e) { if (KeyEvent.VK_ENTER == e.getKeyCode()) { DataContext dataContext = DataManager.getInstance().getDataContext(getTree()); OpenSourceUtil.openSourcesFrom(dataContext, false); } else if (KeyEvent.VK_ESCAPE == e.getKeyCode()) { if (e.isConsumed()) { return; } PsiCopyPasteManager copyPasteManager = PsiCopyPasteManager.getInstance(); boolean[] isCopied = new boolean[1]; if (copyPasteManager.getElements(isCopied) != null && !isCopied[0]) { copyPasteManager.clear(); e.consume(); } } } }); } public void storeState() { if (!isDisposed()) { myStructureViewState = getState(); myFileEditor.putUserData(STRUCTURE_VIEW_STATE_KEY, myStructureViewState); } } public StructureViewState getState() { StructureViewState structureViewState = new StructureViewState(); if (getTree() != null) { structureViewState.setExpandedElements(getExpandedElements()); structureViewState.setSelectedElements(getSelectedElements()); } return structureViewState; } private Object[] getExpandedElements() { final JTree tree = getTree(); if (tree == null) return ArrayUtil.EMPTY_OBJECT_ARRAY; final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree); return convertPathsToValues(expandedPaths.toArray(new TreePath[expandedPaths.size()])); } public void restoreState() { myStructureViewState = myFileEditor.getUserData(STRUCTURE_VIEW_STATE_KEY); if (myStructureViewState == null) { TreeUtil.expand(getTree(), 2); } else { expandStoredElements(); selectStoredElements(); myFileEditor.putUserData(STRUCTURE_VIEW_STATE_KEY, null); myStructureViewState = null; } } private void selectStoredElements() { Object[] selectedPsiElements = null; if (myStructureViewState != null) { selectedPsiElements = myStructureViewState.getSelectedElements(); } if (selectedPsiElements == null) { getTree().setSelectionPath(new TreePath(getRootNode().getPath())); } else { for (Object element : selectedPsiElements) { if (element instanceof PsiElement && !((PsiElement) element).isValid()) { continue; } addSelectionPathTo(element); } } } public void addSelectionPathTo(final Object element) { DefaultMutableTreeNode node = myAbstractTreeBuilder.getNodeForElement(element); if (node != null) { final JTree tree = getTree(); final TreePath path = new TreePath(node.getPath()); if (node == tree.getModel().getRoot() && !tree.isExpanded(path)) tree.expandPath(path); tree.addSelectionPath(path); } } private DefaultMutableTreeNode getRootNode() { return (DefaultMutableTreeNode) getTree().getModel().getRoot(); } private void expandStoredElements() { Object[] expandedPsiElements = null; if (myStructureViewState != null) { expandedPsiElements = myStructureViewState.getExpandedElements(); } if (expandedPsiElements == null) { getTree().expandPath(new TreePath(getRootNode().getPath())); } else { for (Object element : expandedPsiElements) { if (element instanceof PsiElement && !((PsiElement) element).isValid()) { continue; } expandPathToElement(element); } } } protected ActionGroup createActionGroup() { DefaultActionGroup result = new DefaultActionGroup(); Sorter[] sorters = myTreeModel.getSorters(); for (final Sorter sorter : sorters) { if (sorter.isVisible()) { result.add(new TreeActionWrapper(sorter, this)); } } if (sorters.length > 0) { result.addSeparator(); } Grouper[] groupers = myTreeModel.getGroupers(); for (Grouper grouper : groupers) { result.add(new TreeActionWrapper(grouper, this)); } Filter[] filters = myTreeModel.getFilters(); for (Filter filter : filters) { result.add(new TreeActionWrapper(filter, this)); } if (myTreeModel instanceof ProvidingTreeModel) { final Collection<NodeProvider> providers = ((ProvidingTreeModel) myTreeModel).getNodeProviders(); for (NodeProvider provider : providers) { result.add(new TreeActionWrapper(provider, this)); } } result.add(new ExpandAllAction(getTree())); result.add(new CollapseAllAction(getTree())); if (showScrollToFromSourceActions()) { result.addSeparator(); result.add(myAutoScrollToSourceHandler.createToggleAction()); result.add(myAutoScrollFromSourceHandler.createToggleAction()); } return result; } protected boolean showScrollToFromSourceActions() { return true; } public FileEditor getFileEditor() { return myFileEditor; } public AsyncResult<AbstractTreeNode> expandPathToElement(Object element) { if (myAbstractTreeBuilder == null) return new AsyncResult.Rejected<AbstractTreeNode>(); ArrayList<AbstractTreeNode> pathToElement = getPathToElement(element); if (pathToElement.isEmpty()) return new AsyncResult.Rejected<AbstractTreeNode>(); final AsyncResult<AbstractTreeNode> result = new AsyncResult<AbstractTreeNode>(); final AbstractTreeNode toExpand = pathToElement.get(pathToElement.size() - 1); myAbstractTreeBuilder.expand( toExpand, new Runnable() { public void run() { result.setDone(toExpand); } }); return result; } public boolean select(final Object element, final boolean requestFocus) { myAbstractTreeBuilder .getReady(this) .doWhenDone( new Runnable() { public void run() { expandPathToElement(element) .doWhenDone( new AsyncResult.Handler<AbstractTreeNode>() { public void run(AbstractTreeNode abstractTreeNode) { myAbstractTreeBuilder.select( abstractTreeNode, new Runnable() { public void run() { if (requestFocus) { IdeFocusManager.getInstance(myProject) .requestFocus(myAbstractTreeBuilder.getTree(), false); } } }); } }); } }); return true; } private ArrayList<AbstractTreeNode> getPathToElement(Object element) { ArrayList<AbstractTreeNode> result = new ArrayList<AbstractTreeNode>(); final AbstractTreeStructure treeStructure = myAbstractTreeBuilder.getTreeStructure(); if (treeStructure != null) { addToPath( (AbstractTreeNode) treeStructure.getRootElement(), element, result, new THashSet<Object>()); } return result; } private static boolean addToPath( AbstractTreeNode<?> rootElement, Object element, ArrayList<AbstractTreeNode> result, Collection<Object> processedElements) { Object value = rootElement.getValue(); if (value instanceof StructureViewTreeElement) { value = ((StructureViewTreeElement) value).getValue(); } if (!processedElements.add(value)) { return false; } if (Comparing.equal(value, element)) { result.add(0, rootElement); return true; } Collection<? extends AbstractTreeNode> children = rootElement.getChildren(); for (AbstractTreeNode child : children) { if (addToPath(child, element, result, processedElements)) { result.add(0, rootElement); return true; } } return false; } private static DefaultMutableTreeNode findInChildren( DefaultMutableTreeNode currentTreeNode, AbstractTreeNode topPathElement) { for (int i = 0; i < currentTreeNode.getChildCount(); i++) { TreeNode child = currentTreeNode.getChildAt(i); if (((DefaultMutableTreeNode) child).getUserObject().equals(topPathElement)) { return (DefaultMutableTreeNode) child; } } return null; } private void scrollToSelectedElement() { if (myAutoscrollFeedback) { myAutoscrollFeedback = false; return; } StructureViewFactoryImpl structureViewFactory = (StructureViewFactoryImpl) StructureViewFactoryEx.getInstance(myProject); if (!structureViewFactory.getState().AUTOSCROLL_FROM_SOURCE) { return; } myAutoscrollAlarm.cancelAllRequests(); myAutoscrollAlarm.addRequest( new Runnable() { public void run() { if (myAbstractTreeBuilder == null) { return; } try { selectViewableElement(); } catch (IndexNotReadyException ignore) { } } }, 1000); } private void selectViewableElement() { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); final Object currentEditorElement = myTreeModel.getCurrentEditorElement(); if (currentEditorElement != null) { select(currentEditorElement, false); } } public void dispose() { LOG.assertTrue(EventQueue.isDispatchThread(), Thread.currentThread().getName()); myAbstractTreeBuilder = null; // this will also dispose wrapped TreeModel myTreeModelWrapper.dispose(); myFileEditor = null; } public boolean isDisposed() { return myAbstractTreeBuilder == null; } public void centerSelectedRow() { TreePath path = getTree().getSelectionPath(); if (path == null) { return; } myAutoScrollToSourceHandler.setShouldAutoScroll(false); TreeUtil.showRowCentered(getTree(), getTree().getRowForPath(path), false); myAutoScrollToSourceHandler.setShouldAutoScroll(true); } public void setActionActive(String name, boolean state) { StructureViewFactoryEx.getInstanceEx(myProject).setActiveAction(name, state); rebuild(); TreeUtil.expand(getTree(), 2); } protected void rebuild() { storeState(); ++ourSettingsModificationCount; ((SmartTreeStructure) myAbstractTreeBuilder.getTreeStructure()).rebuildTree(); myAbstractTreeBuilder.updateFromRoot(); restoreState(); } public boolean isActionActive(String name) { return !myProject.isDisposed() && StructureViewFactoryEx.getInstanceEx(myProject).isActionActive(name); } public AbstractTreeStructure getTreeStructure() { return myAbstractTreeBuilder.getTreeStructure(); } public JTree getTree() { return myTree; } private final class MyAutoScrollToSourceHandler extends AutoScrollToSourceHandler { private boolean myShouldAutoScroll = true; public void setShouldAutoScroll(boolean shouldAutoScroll) { myShouldAutoScroll = shouldAutoScroll; } protected boolean isAutoScrollMode() { return myShouldAutoScroll && !myProject.isDisposed() && ((StructureViewFactoryImpl) StructureViewFactory.getInstance(myProject)) .getState() .AUTOSCROLL_MODE; } protected void setAutoScrollMode(boolean state) { ((StructureViewFactoryImpl) StructureViewFactory.getInstance(myProject)) .getState() .AUTOSCROLL_MODE = state; } protected void scrollToSource(Component tree) { if (myAbstractTreeBuilder == null) return; myAutoscrollFeedback = true; Navigatable editSourceDescriptor = PlatformDataKeys.NAVIGATABLE.getData(DataManager.getInstance().getDataContext(getTree())); if (myFileEditor != null && editSourceDescriptor != null && editSourceDescriptor.canNavigateToSource()) { editSourceDescriptor.navigate(false); } } } private class MyAutoScrollFromSourceHandler extends AutoScrollFromSourceHandler { private FileEditorPositionListener myFileEditorPositionListener; private MyAutoScrollFromSourceHandler(Project project, Disposable parentDisposable) { super(project, parentDisposable); } public void install() { addEditorCaretListener(); } public void dispose() { myTreeModel.removeEditorPositionListener(myFileEditorPositionListener); } private void addEditorCaretListener() { myFileEditorPositionListener = new FileEditorPositionListener() { public void onCurrentElementChanged() { scrollToSelectedElement(); } }; myTreeModel.addEditorPositionListener(myFileEditorPositionListener); } protected boolean isAutoScrollMode() { StructureViewFactoryImpl structureViewFactory = (StructureViewFactoryImpl) StructureViewFactory.getInstance(myProject); return structureViewFactory.getState().AUTOSCROLL_FROM_SOURCE; } protected void setAutoScrollMode(boolean state) { StructureViewFactoryImpl structureViewFactory = (StructureViewFactoryImpl) StructureViewFactory.getInstance(myProject); structureViewFactory.getState().AUTOSCROLL_FROM_SOURCE = state; final FileEditor[] selectedEditors = FileEditorManager.getInstance(myProject).getSelectedEditors(); if (selectedEditors.length > 0 && state) { scrollToSelectedElement(); } } } public Object getData(String dataId) { if (LangDataKeys.PSI_ELEMENT.is(dataId)) { TreePath path = getSelectedUniquePath(); if (path == null) return null; DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent(); Object userObject = node.getUserObject(); if (!(userObject instanceof AbstractTreeNode)) return null; AbstractTreeNode descriptor = (AbstractTreeNode) userObject; Object element = descriptor.getValue(); if (element instanceof StructureViewTreeElement) { element = ((StructureViewTreeElement) element).getValue(); } if (!(element instanceof PsiElement)) return null; if (!((PsiElement) element).isValid()) return null; return element; } if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) { return convertToPsiElementsArray(getSelectedElements()); } if (PlatformDataKeys.FILE_EDITOR.is(dataId)) { return myFileEditor; } if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) { return myCopyPasteDelegator.getCutProvider(); } if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) { return myCopyPasteDelegator.getCopyProvider(); } if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) { return myCopyPasteDelegator.getPasteProvider(); } if (PlatformDataKeys.NAVIGATABLE.is(dataId)) { Object[] selectedElements = getSelectedTreeElements(); if (selectedElements == null || selectedElements.length == 0) return null; if (selectedElements[0] instanceof Navigatable) { return selectedElements[0]; } } if (PlatformDataKeys.HELP_ID.is(dataId)) { return getHelpID(); } return super.getData(dataId); } @Nullable private static PsiElement[] convertToPsiElementsArray(final Object[] selectedElements) { if (selectedElements == null) return null; ArrayList<PsiElement> psiElements = new ArrayList<PsiElement>(); for (Object selectedElement : selectedElements) { if (selectedElement instanceof PsiElement && ((PsiElement) selectedElement).isValid()) { psiElements.add((PsiElement) selectedElement); } } return PsiUtilBase.toPsiElementArray(psiElements); } @Nullable private TreePath getSelectedUniquePath() { JTree tree = getTree(); if (tree == null) return null; TreePath[] paths = tree.getSelectionPaths(); return paths == null || paths.length != 1 ? null : paths[0]; } public StructureViewModel getTreeModel() { return myTreeModel; } public boolean navigateToSelectedElement(boolean requestFocus) { return select(myTreeModel.getCurrentEditorElement(), requestFocus); } public void doUpdate() { assert ApplicationManager.getApplication().isUnitTestMode(); myAbstractTreeBuilder.addRootToUpdate(); } // todo [kirillk] dirty hack for discovering invalid psi elements, to delegate it to a proper // place after 8.1 public static boolean isValid(Object treeElement) { if (treeElement instanceof StructureViewTreeElementWrapper) { final StructureViewTreeElementWrapper wrapper = (StructureViewTreeElementWrapper) treeElement; if (wrapper.getValue() instanceof PsiTreeElementBase) { final PsiTreeElementBase psiNode = (PsiTreeElementBase) wrapper.getValue(); return psiNode.isValid(); } } return true; } public static class StructureViewTreeElementWrapper extends TreeElementWrapper implements NodeDescriptorProvidingKey { private long childrenStamp = -1; private long modificationCountForChildren = ourSettingsModificationCount; public StructureViewTreeElementWrapper( Project project, TreeElement value, TreeModel treeModel) { super(project, value, treeModel); } @NotNull public Object getKey() { StructureViewTreeElement element = (StructureViewTreeElement) getValue(); if (element instanceof NodeDescriptorProvidingKey) return ((NodeDescriptorProvidingKey) element).getKey(); Object value = element.getValue(); return value == null ? this : value; } @NotNull public Collection<AbstractTreeNode> getChildren() { if (ourSettingsModificationCount != modificationCountForChildren) { resetChildren(); modificationCountForChildren = ourSettingsModificationCount; } final Object o = unwrapValue(getValue()); long currentStamp; if ((o instanceof PsiElement && ((PsiElement) o).getNode() instanceof CompositeElement && childrenStamp != (currentStamp = ((CompositeElement) ((PsiElement) o).getNode()).getModificationCount())) || (o instanceof ModificationTracker && childrenStamp != (currentStamp = ((ModificationTracker) o).getModificationCount()))) { resetChildren(); childrenStamp = currentStamp; } try { return super.getChildren(); } catch (IndexNotReadyException ignore) { return Collections.emptyList(); } } @Override public boolean isAlwaysShowPlus() { if (getElementInfoProvider() != null) { return getElementInfoProvider().isAlwaysShowsPlus((StructureViewTreeElement) getValue()); } return true; } @Override public boolean isAlwaysLeaf() { if (getElementInfoProvider() != null) { return getElementInfoProvider().isAlwaysLeaf((StructureViewTreeElement) getValue()); } return false; } @Nullable private StructureViewModel.ElementInfoProvider getElementInfoProvider() { if (myTreeModel instanceof StructureViewModel.ElementInfoProvider) { return ((StructureViewModel.ElementInfoProvider) myTreeModel); } else if (myTreeModel instanceof TreeModelWrapper) { StructureViewModel model = ((TreeModelWrapper) myTreeModel).getModel(); if (model instanceof StructureViewModel.ElementInfoProvider) { return (StructureViewModel.ElementInfoProvider) model; } } return null; } protected TreeElementWrapper createChildNode(final TreeElement child) { return new StructureViewTreeElementWrapper(myProject, child, myTreeModel); } @Override protected GroupWrapper createGroupWrapper( final Project project, Group group, final TreeModel treeModel) { return new StructureViewGroup(project, group, treeModel); } public boolean equals(Object o) { if (o instanceof StructureViewTreeElementWrapper) { return Comparing.equal( unwrapValue(getValue()), unwrapValue(((StructureViewTreeElementWrapper) o).getValue())); } else if (o instanceof StructureViewTreeElement) { return Comparing.equal(unwrapValue(getValue()), ((StructureViewTreeElement) o).getValue()); } return false; } private static Object unwrapValue(Object o) { if (o instanceof StructureViewTreeElement) { return ((StructureViewTreeElement) o).getValue(); } else { return o; } } public int hashCode() { final Object o = unwrapValue(getValue()); return o != null ? o.hashCode() : 0; } private class StructureViewGroup extends GroupWrapper { public StructureViewGroup(Project project, Group group, TreeModel treeModel) { super(project, group, treeModel); } @Override protected TreeElementWrapper createChildNode(TreeElement child) { return new StructureViewTreeElementWrapper(getProject(), child, myTreeModel); } @Override protected GroupWrapper createGroupWrapper(Project project, Group group, TreeModel treeModel) { return new StructureViewGroup(project, group, treeModel); } @Override public boolean isAlwaysShowPlus() { return true; } } } public String getHelpID() { return ourHelpID; } public Dimension getCurrentSize() { return myTree.getSize(); } public void setReferenceSizeWhileInitializing(Dimension size) { _setRefSize(size); if (size != null) { myAbstractTreeBuilder .getReady(this) .doWhenDone( new Runnable() { public void run() { _setRefSize(null); } }); } } private void _setRefSize(Dimension size) { myTree.setPreferredSize(size); myTree.setMinimumSize(size); myTree.setMaximumSize(size); myTree.revalidate(); myTree.repaint(); } }
/** @author Gregory Shrago */ public class QuickEditHandler extends DocumentAdapter implements Disposable { private final Project myProject; private final QuickEditAction myAction; private final Editor myEditor; private final Document myOrigDocument; private final Document myNewDocument; private final PsiFile myNewFile; private final LightVirtualFile myNewVirtualFile; private final long myOrigCreationStamp; private EditorWindow mySplittedWindow; private boolean myCommittingToOriginal; private final PsiFile myInjectedFile; private final List<Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>> myMarkers = ContainerUtil.newLinkedList(); @Nullable private final RangeMarker myAltFullRange; private static final Key<String> REPLACEMENT_KEY = Key.create("REPLACEMENT_KEY"); QuickEditHandler( Project project, @NotNull PsiFile injectedFile, final PsiFile origFile, Editor editor, QuickEditAction action) { myProject = project; myEditor = editor; myAction = action; myOrigDocument = editor.getDocument(); Place shreds = InjectedLanguageUtil.getShreds(injectedFile); FileType fileType = injectedFile.getFileType(); Language language = injectedFile.getLanguage(); PsiLanguageInjectionHost.Shred firstShred = ContainerUtil.getFirstItem(shreds); PsiFileFactory factory = PsiFileFactory.getInstance(project); String text = InjectedLanguageManager.getInstance(project).getUnescapedText(injectedFile); String newFileName = StringUtil.notNullize(language.getDisplayName(), "Injected") + " Fragment " + "(" + origFile.getName() + ":" + firstShred.getHost().getTextRange().getStartOffset() + ")" + "." + fileType.getDefaultExtension(); // preserve \r\n as it is done in MultiHostRegistrarImpl myNewFile = factory.createFileFromText(newFileName, language, text, true, false); myNewVirtualFile = ObjectUtils.assertNotNull((LightVirtualFile) myNewFile.getVirtualFile()); myNewVirtualFile.setOriginalFile(origFile.getVirtualFile()); assert myNewFile != null : "PSI file is null"; assert myNewFile.getTextLength() == myNewVirtualFile.getContent().length() : "PSI / Virtual file text mismatch"; myNewVirtualFile.setOriginalFile(origFile.getVirtualFile()); // suppress possible errors as in injected mode myNewFile.putUserData( InjectedLanguageUtil.FRANKENSTEIN_INJECTION, injectedFile.getUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION)); myNewFile.putUserData(FileContextUtil.INJECTED_IN_ELEMENT, shreds.getHostPointer()); myNewDocument = PsiDocumentManager.getInstance(project).getDocument(myNewFile); assert myNewDocument != null; EditorActionManager.getInstance() .setReadonlyFragmentModificationHandler(myNewDocument, new MyQuietHandler()); myOrigCreationStamp = myOrigDocument.getModificationStamp(); // store creation stamp for UNDO tracking myOrigDocument.addDocumentListener(this, this); myNewDocument.addDocumentListener(this, this); EditorFactory editorFactory = ObjectUtils.assertNotNull(EditorFactory.getInstance()); // not FileEditorManager listener because of RegExp checker and alike editorFactory.addEditorFactoryListener( new EditorFactoryAdapter() { int useCount; @Override public void editorCreated(@NotNull EditorFactoryEvent event) { if (event.getEditor().getDocument() != myNewDocument) return; useCount++; } @Override public void editorReleased(@NotNull EditorFactoryEvent event) { if (event.getEditor().getDocument() != myNewDocument) return; if (--useCount > 0) return; if (Boolean.TRUE.equals( myNewVirtualFile.getUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN))) return; Disposer.dispose(QuickEditHandler.this); } }, this); if ("JAVA".equals(firstShred.getHost().getLanguage().getID())) { PsiLanguageInjectionHost.Shred lastShred = ContainerUtil.getLastItem(shreds); myAltFullRange = myOrigDocument.createRangeMarker( firstShred.getHostRangeMarker().getStartOffset(), lastShred.getHostRangeMarker().getEndOffset()); myAltFullRange.setGreedyToLeft(true); myAltFullRange.setGreedyToRight(true); initGuardedBlocks(shreds); myInjectedFile = null; } else { initMarkers(shreds); myAltFullRange = null; myInjectedFile = injectedFile; } } public boolean isValid() { boolean valid = myNewVirtualFile.isValid() && (myAltFullRange == null && myInjectedFile.isValid() || myAltFullRange != null && myAltFullRange.isValid()); if (valid) { for (Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> t : myMarkers) { if (!t.first.isValid() || !t.second.isValid() || t.third.getElement() == null) { valid = false; break; } } } return valid; } public void navigate(int injectedOffset) { if (myAction.isShowInBalloon()) { final JComponent component = myAction.createBalloonComponent(myNewFile); if (component != null) { final Balloon balloon = JBPopupFactory.getInstance() .createBalloonBuilder(component) .setShadow(true) .setAnimationCycle(0) .setHideOnClickOutside(true) .setHideOnKeyOutside(true) .setHideOnAction(false) .setFillColor(UIUtil.getControlColor()) .createBalloon(); new AnAction() { @Override public void actionPerformed(AnActionEvent e) { balloon.hide(); } }.registerCustomShortcutSet(CommonShortcuts.ESCAPE, component); Disposer.register(myNewFile.getProject(), balloon); final Balloon.Position position = QuickEditAction.getBalloonPosition(myEditor); RelativePoint point = JBPopupFactory.getInstance().guessBestPopupLocation(myEditor); if (position == Balloon.Position.above) { final Point p = point.getPoint(); point = new RelativePoint( point.getComponent(), new Point(p.x, p.y - myEditor.getLineHeight())); } balloon.show(point, position); } } else { final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(myProject); final FileEditor[] editors = fileEditorManager.getEditors(myNewVirtualFile); if (editors.length == 0) { final EditorWindow curWindow = fileEditorManager.getCurrentWindow(); mySplittedWindow = curWindow.split(SwingConstants.HORIZONTAL, false, myNewVirtualFile, true); } Editor editor = fileEditorManager.openTextEditor( new OpenFileDescriptor(myProject, myNewVirtualFile, injectedOffset), true); // fold missing values if (editor != null) { editor.putUserData(QuickEditAction.QUICK_EDIT_HANDLER, this); final FoldingModel foldingModel = editor.getFoldingModel(); foldingModel.runBatchFoldingOperation( () -> { for (RangeMarker o : ContainerUtil.reverse(((DocumentEx) myNewDocument).getGuardedBlocks())) { String replacement = o.getUserData(REPLACEMENT_KEY); if (StringUtil.isEmpty(replacement)) continue; FoldRegion region = foldingModel.addFoldRegion(o.getStartOffset(), o.getEndOffset(), replacement); if (region != null) region.setExpanded(false); } }); } SwingUtilities.invokeLater( () -> myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE)); } } @Override public void documentChanged(DocumentEvent e) { UndoManager undoManager = UndoManager.getInstance(myProject); boolean undoOrRedo = undoManager.isUndoInProgress() || undoManager.isRedoInProgress(); if (undoOrRedo) { // allow undo/redo up until 'creation stamp' back in time // and check it after action is completed if (e.getDocument() == myOrigDocument) { //noinspection SSBasedInspection SwingUtilities.invokeLater( () -> { if (myOrigCreationStamp > myOrigDocument.getModificationStamp()) { closeEditor(); } }); } } else if (e.getDocument() == myNewDocument) { commitToOriginal(e); if (!isValid()) { ApplicationManager.getApplication() .invokeLater(() -> closeEditor(), myProject.getDisposed()); } } else if (e.getDocument() == myOrigDocument) { if (myCommittingToOriginal || myAltFullRange != null && myAltFullRange.isValid()) return; ApplicationManager.getApplication().invokeLater(() -> closeEditor(), myProject.getDisposed()); } } private void closeEditor() { boolean unsplit = false; if (mySplittedWindow != null && !mySplittedWindow.isDisposed()) { final EditorWithProviderComposite[] editors = mySplittedWindow.getEditors(); if (editors.length == 1 && Comparing.equal(editors[0].getFile(), myNewVirtualFile)) { unsplit = true; } } FileEditorManager.getInstance(myProject).closeFile(myNewVirtualFile); if (unsplit) { for (EditorWindow editorWindow : mySplittedWindow.findSiblings()) { editorWindow.unsplit(true); } } } public void initMarkers(Place shreds) { SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); int curOffset = -1; for (PsiLanguageInjectionHost.Shred shred : shreds) { final RangeMarker rangeMarker = myNewDocument.createRangeMarker( shred.getRange().getStartOffset() + shred.getPrefix().length(), shred.getRange().getEndOffset() - shred.getSuffix().length()); final TextRange rangeInsideHost = shred.getRangeInsideHost(); PsiLanguageInjectionHost host = shred.getHost(); RangeMarker origMarker = myOrigDocument.createRangeMarker( rangeInsideHost.shiftRight(host.getTextRange().getStartOffset())); SmartPsiElementPointer<PsiLanguageInjectionHost> elementPointer = smartPointerManager.createSmartPsiElementPointer(host); Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> markers = Trinity.<RangeMarker, RangeMarker, SmartPsiElementPointer>create( origMarker, rangeMarker, elementPointer); myMarkers.add(markers); origMarker.setGreedyToRight(true); rangeMarker.setGreedyToRight(true); if (origMarker.getStartOffset() > curOffset) { origMarker.setGreedyToLeft(true); rangeMarker.setGreedyToLeft(true); } curOffset = origMarker.getEndOffset(); } initGuardedBlocks(shreds); } private void initGuardedBlocks(Place shreds) { int origOffset = -1; int curOffset = 0; for (PsiLanguageInjectionHost.Shred shred : shreds) { Segment hostRangeMarker = shred.getHostRangeMarker(); int start = shred.getRange().getStartOffset() + shred.getPrefix().length(); int end = shred.getRange().getEndOffset() - shred.getSuffix().length(); if (curOffset < start) { RangeMarker guard = myNewDocument.createGuardedBlock(curOffset, start); if (curOffset == 0 && shred == shreds.get(0)) guard.setGreedyToLeft(true); String padding = origOffset < 0 ? "" : myOrigDocument.getText().substring(origOffset, hostRangeMarker.getStartOffset()); guard.putUserData(REPLACEMENT_KEY, fixQuotes(padding)); } curOffset = end; origOffset = hostRangeMarker.getEndOffset(); } if (curOffset < myNewDocument.getTextLength()) { RangeMarker guard = myNewDocument.createGuardedBlock(curOffset, myNewDocument.getTextLength()); guard.setGreedyToRight(true); guard.putUserData(REPLACEMENT_KEY, ""); } } private void commitToOriginal(final DocumentEvent e) { VirtualFile origVirtualFile = PsiUtilCore.getVirtualFile(myNewFile.getContext()); myCommittingToOriginal = true; try { if (origVirtualFile == null || !ReadonlyStatusHandler.getInstance(myProject) .ensureFilesWritable(origVirtualFile) .hasReadonlyFiles()) { PostprocessReformattingAspect.getInstance(myProject) .disablePostprocessFormattingInside( () -> { if (myAltFullRange != null) { altCommitToOriginal(e); return; } commitToOriginalInner(); }); PsiDocumentManager.getInstance(myProject) .doPostponedOperationsAndUnblockDocument(myOrigDocument); } } finally { myCommittingToOriginal = false; } } private void commitToOriginalInner() { final String text = myNewDocument.getText(); final Map< PsiLanguageInjectionHost, Set<Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>>> map = ContainerUtil.classify( myMarkers.iterator(), new Convertor< Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>, PsiLanguageInjectionHost>() { @Override public PsiLanguageInjectionHost convert( final Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> o) { final PsiElement element = o.third.getElement(); return (PsiLanguageInjectionHost) element; } }); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); documentManager.commitDocument(myOrigDocument); // commit here and after each manipulator update int localInsideFileCursor = 0; for (PsiLanguageInjectionHost host : map.keySet()) { if (host == null) continue; String hostText = host.getText(); ProperTextRange insideHost = null; StringBuilder sb = new StringBuilder(); for (Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> entry : map.get(host)) { RangeMarker origMarker = entry.first; // check for validity? int hostOffset = host.getTextRange().getStartOffset(); ProperTextRange localInsideHost = new ProperTextRange( origMarker.getStartOffset() - hostOffset, origMarker.getEndOffset() - hostOffset); RangeMarker rangeMarker = entry.second; ProperTextRange localInsideFile = new ProperTextRange( Math.max(localInsideFileCursor, rangeMarker.getStartOffset()), rangeMarker.getEndOffset()); if (insideHost != null) { // append unchanged inter-markers fragment sb.append( hostText.substring(insideHost.getEndOffset(), localInsideHost.getStartOffset())); } sb.append( localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty() ? localInsideFile.substring(text) : ""); localInsideFileCursor = localInsideFile.getEndOffset(); insideHost = insideHost == null ? localInsideHost : insideHost.union(localInsideHost); } assert insideHost != null; ElementManipulators.getManipulator(host).handleContentChange(host, insideHost, sb.toString()); documentManager.commitDocument(myOrigDocument); } } private void altCommitToOriginal(@NotNull DocumentEvent e) { final PsiFile origPsiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myOrigDocument); String newText = myNewDocument.getText(); // prepare guarded blocks LinkedHashMap<String, String> replacementMap = new LinkedHashMap<String, String>(); int count = 0; for (RangeMarker o : ContainerUtil.reverse(((DocumentEx) myNewDocument).getGuardedBlocks())) { String replacement = o.getUserData(REPLACEMENT_KEY); String tempText = "REPLACE" + (count++) + Long.toHexString(StringHash.calc(replacement)); newText = newText.substring(0, o.getStartOffset()) + tempText + newText.substring(o.getEndOffset()); replacementMap.put(tempText, replacement); } // run preformat processors final int hostStartOffset = myAltFullRange.getStartOffset(); myEditor.getCaretModel().moveToOffset(hostStartOffset); for (CopyPastePreProcessor preProcessor : Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) { newText = preProcessor.preprocessOnPaste(myProject, origPsiFile, myEditor, newText, null); } myOrigDocument.replaceString(hostStartOffset, myAltFullRange.getEndOffset(), newText); // replace temp strings for guarded blocks for (String tempText : replacementMap.keySet()) { int idx = CharArrayUtil.indexOf( myOrigDocument.getCharsSequence(), tempText, hostStartOffset, myAltFullRange.getEndOffset()); myOrigDocument.replaceString(idx, idx + tempText.length(), replacementMap.get(tempText)); } // JAVA: fix occasional char literal concatenation fixDocumentQuotes(myOrigDocument, hostStartOffset - 1); fixDocumentQuotes(myOrigDocument, myAltFullRange.getEndOffset()); // reformat PsiDocumentManager.getInstance(myProject).commitDocument(myOrigDocument); Runnable task = () -> { try { CodeStyleManager.getInstance(myProject) .reformatRange(origPsiFile, hostStartOffset, myAltFullRange.getEndOffset(), true); } catch (IncorrectOperationException e1) { // LOG.error(e); } }; DocumentUtil.executeInBulk(myOrigDocument, true, task); PsiElement newInjected = InjectedLanguageManager.getInstance(myProject) .findInjectedElementAt(origPsiFile, hostStartOffset); DocumentWindow documentWindow = newInjected == null ? null : InjectedLanguageUtil.getDocumentWindow(newInjected); if (documentWindow != null) { myEditor.getCaretModel().moveToOffset(documentWindow.injectedToHost(e.getOffset())); myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); } } private static String fixQuotes(String padding) { if (padding.isEmpty()) return padding; if (padding.startsWith("'")) padding = '\"' + padding.substring(1); if (padding.endsWith("'")) padding = padding.substring(0, padding.length() - 1) + "\""; return padding; } private static void fixDocumentQuotes(Document doc, int offset) { if (doc.getCharsSequence().charAt(offset) == '\'') { doc.replaceString(offset, offset + 1, "\""); } } @Override public void dispose() { // noop } @TestOnly public PsiFile getNewFile() { return myNewFile; } public boolean changesRange(TextRange range) { if (myAltFullRange != null) { return range.intersects(myAltFullRange.getStartOffset(), myAltFullRange.getEndOffset()); } else if (!myMarkers.isEmpty()) { TextRange hostRange = TextRange.create( myMarkers.get(0).first.getStartOffset(), myMarkers.get(myMarkers.size() - 1).first.getEndOffset()); return range.intersects(hostRange); } return false; } private static class MyQuietHandler implements ReadonlyFragmentModificationHandler { @Override public void handle(final ReadOnlyFragmentModificationException e) { // nothing } } }
/** * Parse the section file and fill in the Dbase. * * @return Success code */ public void parse() throws Exception { JOAParameter tempProperties[] = new JOAParameter[100]; FileInputStream in = null; DataInputStream inData = null; short inShort = 0; long bytesRead = 0; long bytesInFile = mFile.length(); short[] sarray = new short[1]; int mTotalStns = 0; EPSProgressDialog mProgress = new EPSProgressDialog(new Frame(), mProgressStr, Color.blue); mProgress.setVisible(true); // Get an epic key database specific to JOA EPIC_Key_DB mEpicKeyDB = new EPIC_Key_DB("joa_epic.key"); // Get an epic key database specific to JOA EPIC_Key_DB mOrigEpicKeyDB = new EPIC_Key_DB("epic.key"); // create a vector for temporary storage of the dbases Vector dBases = new Vector(100); try { in = new FileInputStream(mFile); BufferedInputStream bis = new BufferedInputStream(in, 1000000); inData = new DataInputStream(bis); // read version short vers = inData.readShort(); bytesRead += 2; if (vers < 2) { FileImportException fiex = new FileImportException(); String errStr = "Invalid version for a JOA binary file"; fiex.setErrorType(errStr); throw fiex; } // read number of bytes in file description string inShort = inData.readShort(); bytesRead += 2; // read the file description String byte buf[] = new byte[inShort]; inData.read(buf, 0, inShort); String fileDescrip = new String(buf); bytesRead += inShort; // create a new open file object JOADataFile of = new JOADataFile(fileDescrip); // read the number of sections int numSections = inData.readShort(); bytesRead += 2; // read each section JOASection sech; int ord = 0; for (int s = 0; s < numSections; s++) { mProgress.setPercentComplete(100.0 * ((double) bytesRead / (double) bytesInFile)); // read the section header inShort = inData.readShort(); bytesRead += 2; byte buf1[] = new byte[inShort]; inData.read(buf1, 0, inShort); String sectionDescrip = new String(buf1); bytesRead += inShort; // read the ship code byte bufsc[] = new byte[2]; bufsc[0] = inData.readByte(); bufsc[1] = inData.readByte(); String shipCode = new String(bufsc); bytesRead += 2; // read num casts int numCasts = inData.readShort(); bytesRead += 2; // read num parameters int numVars = inData.readShort(); bytesRead += 2; // quality code int qcStd = 1; if (vers == 4) { qcStd = inData.readShort(); bytesRead += 2; } // create a new section of.mNumSections++; sech = new JOASection(of.mNumSections, sectionDescrip, shipCode, numCasts, numVars); if (vers == 2) { // read the properties byte bufv[] = new byte[4]; for (int p = 0; p < numVars; p++) { // parameter name inData.read(bufv, 0, 4); String tempVar = new String(bufv); bytesRead += 4; // units inShort = inData.readShort(); bytesRead += 2; String units = null; if (inShort > 0) { byte buf11[] = new byte[inShort]; inData.read(buf11, 0, inShort); units = new String(buf11); bytesRead += inShort; } // convert varnames to UC tempVar.toUpperCase(); // create new property tempProperties[p] = new JOAParameter(tempVar, units); // add this property to the section property list if (tempProperties[p].mUnits == null) { tempProperties[p].mUnits = EPS_Util.paramNameToJOAUnits(false, tempProperties[0].mVarLabel); } if (tempProperties[p].mUnits == null) { tempProperties[p].mUnits = new String("na"); } tempProperties[p].mCastOrObs = EPSConstants.ALL_OBS; // read the actual scale int actScale = inData.readShort(); bytesRead += 2; if (actScale != 0) tempProperties[p].mActScale = 1.0 / (double) actScale; else tempProperties[p].mActScale = 1.0; // read the actual origin int actOrigin = inData.readShort(); tempProperties[p].mActOrigin = (double) actOrigin * tempProperties[p].mActScale; // read reverse y int reverseY = inData.readShort(); bytesRead += 2; if (reverseY == 0) tempProperties[p].mReverseY = false; else tempProperties[p].mReverseY = true; tempProperties[p].mWasCalculated = false; } } else { // read the properties String tempVar = null; for (int p = 0; p < numVars; p++) { // length of parameter name inShort = inData.readShort(); bytesRead += 2; // parameter name if (inShort > 0) { byte buf13[] = new byte[inShort]; inData.read(buf13, 0, inShort); tempVar = new String(buf13); bytesRead += inShort; } // units inShort = inData.readShort(); bytesRead += 2; String units = null; if (inShort > 0) { byte buf11[] = new byte[inShort]; inData.read(buf11, 0, inShort); units = new String(buf11); bytesRead += inShort; } // convert varnames to UC tempVar = tempVar.toUpperCase(); // create new property tempProperties[p] = new JOAParameter(tempVar, units); // add this property to the section property list if (tempProperties[p].mUnits == null) { tempProperties[p].mUnits = EPS_Util.paramNameToJOAUnits(false, tempProperties[0].mVarLabel); } if (tempProperties[p].mUnits == null) { tempProperties[p].mUnits = new String("na"); } tempProperties[p].mCastOrObs = EPSConstants.ALL_OBS; // read the actual scale int actScale = inData.readShort(); bytesRead += 2; if (actScale != 0) tempProperties[p].mActScale = 1.0 / (double) actScale; else tempProperties[p].mActScale = 1.0; // read the actual origin int actOrigin = inData.readShort(); tempProperties[p].mActOrigin = (double) actOrigin * tempProperties[p].mActScale; // read reverse y int reverseY = inData.readShort(); bytesRead += 2; if (reverseY == 0) tempProperties[p].mReverseY = false; else tempProperties[p].mReverseY = true; tempProperties[p].mWasCalculated = false; } } // read the cast headers for (int c = 0; c < numCasts; c++) { // read station number inShort = inData.readShort(); bytesRead += 2; byte bufx[] = new byte[inShort]; inData.read(bufx, 0, inShort); String stnNum = new String(bufx); bytesRead += inShort; // read cast number int castNum = inData.readShort(); bytesRead += 2; double myLat = 0.0; double myLon = 0.0; if (vers == 2) { // read latitude/lon int lat = inData.readInt(); bytesRead += 4; int lon = inData.readInt(); bytesRead += 4; myLat = lat * 0.001; myLon = lon * 0.001; } else if (vers > 2) { // read latitude/lon myLat = inData.readDouble(); bytesRead += 8; myLon = inData.readDouble(); bytesRead += 4; } // read number of bottles int numBottles = inData.readShort(); bytesRead += 2; // read the date int year = inData.readInt(); bytesRead += 4; int month = inData.readInt(); bytesRead += 4; int day = inData.readInt(); bytesRead += 4; int hour = inData.readInt(); bytesRead += 4; double min = inData.readDouble(); bytesRead += 8; // read bottom int bottomdbar = inData.readInt(); // read station quality int stnQual = inData.readShort(); bytesRead += 2; ord++; JOAStation sh = new JOAStation( ord, shipCode, stnNum, castNum, myLat, myLon, numBottles, year, month, day, hour, min, bottomdbar, stnQual); sech.mStations.addElement(sh); sh.setType("JOA BOTTLE"); // make a DBase object Dbase db = new Dbase(); // add the global attributes db.addEPSAttribute( "CRUISE", EPCHAR, sech.mSectionDescription.length(), sech.mSectionDescription); db.addEPSAttribute("CAST", EPCHAR, sh.mStnNum.length(), sh.mStnNum); sarray[0] = (short) sh.mBottomDepthInDBARS; db.addEPSAttribute("WATER_DEPTH", EPSHORT, 1, sarray); db.addEPSAttribute("DATA_ORIGIN", EPCHAR, sech.mShipCode.length(), sech.mShipCode); String dType = sh.getType(); if (dType == null) dType = "UNKN"; db.addEPSAttribute("DATA_TYPE", EPCHAR, dType.length(), dType); sarray[0] = (short) stnQual; db.addEPSAttribute("STN_QUALITY", EPSHORT, 1, sarray); db.setDataType("JOA BOTTLE"); // add to temporary collection dBases.addElement(db); } int start = mTotalStns; int end = sech.mStations.size() + mTotalStns; for (int sc = start; sc < end; sc++) { mProgress.setPercentComplete(100.0 * ((double) bytesRead / (double) bytesInFile)); // get a dBase Dbase db = (Dbase) dBases.elementAt(sc); // get a station JOAStation sh = (JOAStation) sech.mStations.elementAt(sc - start); // create an array of arrays to store the data double[][] va = new double[sech.mNumVars][sh.mNumBottles]; int[][] qc = new int[sech.mNumVars][sh.mNumBottles]; short[] bqc = new short[sh.mNumBottles]; int presPos = 0; // read the bottles for (int b = 0; b < sh.mNumBottles; b++) { // read the bottle quality code bqc[b] = inData.readShort(); bytesRead += 2; for (int v = 0; v < sech.mNumVars; v++) { // store the position of the PRES variable if (tempProperties[v].mVarLabel.equals("PRES")) presPos = v; // get the measured parameter double dVarVal = EPSConstants.MISSINGVALUE; try { dVarVal = inData.readDouble(); } catch (IOException e) { FileImportException fiex = new FileImportException(); String errStr = "Error reading the parameter data. " + "\n" + "Bottle #" + b + " Parameter #" + v; fiex.setErrorType(errStr); throw fiex; } bytesRead += 8; // store the value in the multidimensional array va[v][b] = dVarVal; // get the quality flag short flag = (short) EPSConstants.MISSINGVALUE; try { flag = inData.readShort(); } catch (IOException e) { FileImportException fiex = new FileImportException(); String errStr = "Error reading the parameter quality code. " + "\n" + "Bottle #" + b + " Parameter #" + v; fiex.setErrorType(errStr); throw fiex; } bytesRead += 2; qc[v][b] = flag; } // for v } // for b // add the bottle quality codes as an attribute db.addEPSAttribute("BOTTLE_QUALITY_CODES", EPSHORT, sh.mNumBottles, bqc); // create the axes time = 0, depth = 1, lat = 2, lon = 3 Axis timeAxis = new Axis(); Axis zAxis = new Axis(); Axis latAxis = new Axis(); Axis lonAxis = new Axis(); // time axis timeAxis.setName("time"); timeAxis.setTime(true); timeAxis.setUnlimited(false); timeAxis.setAxisType(EPTAXIS); timeAxis.setLen(1); int hour = 0; if (sh.mHour != EPSConstants.MISSINGVALUE) hour = sh.mHour; double mins = 0; if (sh.mMinute != EPSConstants.MISSINGVALUE) mins = sh.mMinute; // make the time axis units String date = "days since "; int min = (int) mins; double fmin = mins - min; int secs = (int) (fmin * 60.0); double fsec = (fmin * 60.0) - secs; int msec = (int) (fsec * 1000.0); String fs = String.valueOf(fsec); fs = fs.substring(fs.indexOf(".") + 1, fs.length()).trim(); int f = 0; if (fs != null && fs.length() > 0) f = Integer.valueOf(fs).intValue(); // sprintf(time_string,"%04d-%02d-%02d %02d:%02d:%02d.%03d",yr,mon,day,hr,min,sec,f); String frmt = new String( "{0,number,####}-{1,number,00}-{2,number,00} {3,number,00}:{4,number,00}:{5,number,00}.{6,number,000}"); MessageFormat msgf = new MessageFormat(frmt); Object[] objs = { new Integer(sh.mYear), new Integer(sh.mMonth), new Integer(sh.mDay), new Integer(hour), new Integer(min), new Integer(secs), new Integer(f) }; StringBuffer out = new StringBuffer(); msgf.format(objs, out, null); String time_string = new String(out); date = date + time_string; timeAxis.addAttribute(0, "units", EPCHAR, date.length(), date); timeAxis.setUnits(date); GeoDate gd = null; try { gd = new GeoDate(sh.mMonth, sh.mDay, sh.mYear, hour, min, secs, msec); GeoDate[] ta = {gd}; MultiArray tma = new ArrayMultiArray(ta); timeAxis.setData(tma); db.setAxis(timeAxis); } catch (Exception ex) { GeoDate[] ta = {new GeoDate()}; MultiArray tma = new ArrayMultiArray(ta); timeAxis.setData(tma); db.setAxis(timeAxis); } // add the time axes variable EPSVariable var = new EPSVariable(); var.setOname("time"); var.setDtype(EPDOUBLE); var.setVclass(Double.TYPE); var.addAttribute(0, "units", EPCHAR, date.length(), date); var.setUnits(date); double[] vta = {0.0}; MultiArray vtma = new ArrayMultiArray(vta); try { var.setData(vtma); } catch (Exception ex) { } db.addEPSVariable(var); // z axis zAxis.setName("depth"); zAxis.setTime(false); zAxis.setUnlimited(false); zAxis.setLen(sh.mNumBottles); zAxis.setAxisType(EPZAXIS); zAxis.addAttribute(0, "FORTRAN_format", EPCHAR, 5, "f10.1"); zAxis.addAttribute(1, "units", EPCHAR, 4, "dbar"); zAxis.setUnits("dbar"); zAxis.setFrmt("f10.1"); // zAxis.addAttribute(2, "type", EPCHAR, 0, ""); sarray[0] = 1; zAxis.addAttribute(2, "epic_code", EPSHORT, 1, sarray); double[] za = new double[sh.mNumBottles]; for (int b = 0; b < sh.mNumBottles; b++) { za[b] = va[presPos][b]; } MultiArray zma = new ArrayMultiArray(za); zAxis.setData(zma); db.setAxis(zAxis); // add the z axes variables var = new EPSVariable(); var.setOname("depth"); var.setDtype(EPDOUBLE); var.setVclass(Double.TYPE); var.addAttribute(0, "FORTRAN_format", EPCHAR, 5, "f10.1"); var.addAttribute(1, "units", EPCHAR, 4, "dbar"); var.setUnits("dbar"); var.setFrmt("f10.1"); // var.addAttribute(2, "type", EPCHAR, 0, ""); sarray[0] = 1; var.addAttribute(2, "epic_code", EPSHORT, 1, sarray); MultiArray zvma = new ArrayMultiArray(za); try { var.setData(zvma); } catch (Exception ex) { } db.addEPSVariable(var); // lat axis latAxis.setName("latitude"); latAxis.setTime(false); latAxis.setUnlimited(false); latAxis.setLen(1); latAxis.setAxisType(EPYAXIS); latAxis.addAttribute(0, "FORTRAN_format", EPCHAR, 5, "f10.4"); latAxis.addAttribute(1, "units", EPCHAR, 7, "degrees"); latAxis.setUnits("degrees"); latAxis.setFrmt("f10.4"); // latAxis.addAttribute(2, "type", EPCHAR, 0, ""); sarray[0] = 500; latAxis.addAttribute(2, "epic_code", EPSHORT, 1, sarray); double lat = sh.mLat; double[] la = {lat}; MultiArray lma = new ArrayMultiArray(la); latAxis.setData(lma); db.setAxis(latAxis); // add the y axes variable var = new EPSVariable(); var.setOname("latitude"); var.setDtype(EPDOUBLE); var.setVclass(Double.TYPE); var.addAttribute(0, "FORTRAN_format", EPCHAR, 5, "f10.4"); var.addAttribute(1, "units", EPCHAR, 7, "degrees"); var.setUnits("degrees"); var.setFrmt("f10.4"); // var.addAttribute(2, "type", EPCHAR, 0, ""); sarray[0] = 500; var.addAttribute(2, "epic_code", EPSHORT, 1, sarray); MultiArray yvma = new ArrayMultiArray(la); try { var.setData(yvma); } catch (Exception ex) { } db.addEPSVariable(var); // lon axis lonAxis.setName("longitude"); lonAxis.setTime(false); lonAxis.setUnlimited(false); lonAxis.setLen(1); lonAxis.setAxisType(EPXAXIS); lonAxis.addAttribute(0, "FORTRAN_format", EPCHAR, 5, "f10.4"); lonAxis.addAttribute(1, "units", EPCHAR, 7, "degrees"); lonAxis.setUnits("degrees"); lonAxis.setFrmt("f10.4"); // lonAxis.addAttribute(2, "type", EPCHAR, 0, ""); sarray[0] = 502; lonAxis.addAttribute(2, "epic_code", EPSHORT, 1, sarray); double lon = sh.mLon; double[] lla = {lon}; lma = new ArrayMultiArray(lla); lonAxis.setData(lma); db.setAxis(lonAxis); // add the x axes variable var = new EPSVariable(); var.setOname("longitude"); var.setDtype(EPDOUBLE); var.setVclass(Double.TYPE); var.addAttribute(0, "FORTRAN_format", EPCHAR, 5, "f10.4"); var.addAttribute(1, "units", EPCHAR, 7, "degrees"); var.setUnits("degrees"); var.setFrmt("f10.4"); // var.addAttribute(2, "type", EPCHAR, 0, ""); sarray[0] = 502; var.addAttribute(2, "epic_code", EPSHORT, 1, sarray); MultiArray xvma = new ArrayMultiArray(lla); try { var.setData(xvma); } catch (Exception ex) { } db.addEPSVariable(var); // add the measured variables for (int v = 0; v < sech.mNumVars; v++) { // if (presPos == v) // continue; // create an array of measured EPSVariables for this database EPSVariable epsVar = new EPSVariable(); // initialize the new EPSVariables // look this variable up in JOA EPIC_Key. find matching entry in original EPIC Key String oname = tempProperties[v].mVarLabel; String sname = null; String lname = null; String gname = null; String units = null; String ffrmt = null; int keyID = -99; int type = -99; try { keyID = mEpicKeyDB.findKey(tempProperties[v].mVarLabel); Key key = mOrigEpicKeyDB.findKey(keyID); gname = key.getGname(); sname = key.getSname(); lname = key.getLname(); units = key.getUnits(); ffrmt = key.getFrmt(); type = key.getType(); } catch (Exception e) { lname = tempProperties[v].mVarLabel; gname = tempProperties[v].mVarLabel; sname = tempProperties[v].mVarLabel; units = tempProperties[v].mUnits; } // make a new variable epsVar = new EPSVariable(); epsVar.setOname(oname); epsVar.setSname(sname); epsVar.setLname(lname); epsVar.setGname(gname); epsVar.setDtype(EPDOUBLE); epsVar.setVclass(Double.TYPE); int numAttributes = 0; if (ffrmt != null) { epsVar.addAttribute(numAttributes++, "FORTRAN_format", EPCHAR, ffrmt.length(), ffrmt); epsVar.setFrmt(ffrmt); } if (units != null && units.length() > 0) { epsVar.addAttribute(numAttributes++, "units", EPCHAR, units.length(), units); epsVar.setUnits(units); } if (keyID >= 0) { sarray[0] = (short) type; // epsVar.addAttribute(numAttributes++, "type", EPSHORT, 1, sarray); } if (keyID >= 0) { sarray[0] = (short) keyID; epsVar.addAttribute(numAttributes++, "epic_code", EPSHORT, 1, sarray); } // add the quality code attribute String qcVar = oname + "_QC"; epsVar.addAttribute(numAttributes++, "OBS_QC_VARIABLE", EPCHAR, qcVar.length(), qcVar); // connect variable to axis epsVar.setDimorder(0, 0); epsVar.setDimorder(1, 1); epsVar.setDimorder(2, 2); epsVar.setDimorder(3, 3); epsVar.setT(timeAxis); epsVar.setZ(zAxis); epsVar.setY(latAxis); epsVar.setX(lonAxis); // set the data // create storage for the measured variables double[][][][] vaa = new double[1][sh.mNumBottles][1][1]; for (int b = 0; b < sh.mNumBottles; b++) { vaa[0][b][0][0] = va[v][b]; } MultiArray mdma = new ArrayMultiArray(vaa); try { epsVar.setData(mdma); } catch (Exception ex) { System.out.println("throwing"); } // add the variable to the database db.addEPSVariable(epsVar); // create the quality code variable epsVar = new EPSVariable(); epsVar.setOname(oname + "_QC"); epsVar.setSname(sname + "_QC"); epsVar.setLname(sname + "_QC"); epsVar.setGname(sname + "_QC"); epsVar.setDtype(EPSHORT); epsVar.setVclass(Short.TYPE); // connect variable to axis epsVar.setDimorder(0, 0); epsVar.setDimorder(1, 1); epsVar.setDimorder(2, 2); epsVar.setDimorder(3, 3); epsVar.setT(timeAxis); epsVar.setZ(zAxis); epsVar.setY(latAxis); epsVar.setX(lonAxis); // set the data // create storage for the qc variables short[][][][] qcaa = new short[1][sh.mNumBottles][1][1]; for (int b = 0; b < sh.mNumBottles; b++) { qcaa[0][b][0][0] = (short) qc[v][b]; } MultiArray qcma = new ArrayMultiArray(qcaa); try { epsVar.setData(qcma); } catch (Exception ex) { System.out.println("throwing"); } // add the qc variable to the database db.addEPSVariable(epsVar); } // for v } mTotalStns += numCasts; } // for sc // read the file comments StringBuffer sb = null; while (true) { try { // read continuation line inShort = inData.readShort(); bytesRead += 2; if (inShort == 1) { // read number of bytes in file description string inShort = inData.readShort(); bytesRead += 2; // read the file description String byte buf2[] = new byte[inShort]; inData.read(buf2, 0, inShort); String commentLine = new String(buf2); bytesRead += inShort; if (sb == null) sb = new StringBuffer(commentLine); else sb.append(commentLine); } } catch (IOException e) { break; } } // while true if (sb != null) { // make attributes for the comments mOwnerDBase.setDataComment(new String(sb)); } // make a sub database in the dbase mOwnerDBase.createSubEntries(mTotalStns, mFile.getName()); for (int d = 0; d < mTotalStns; d++) { Dbase db = (Dbase) dBases.elementAt(d); mOwnerDBase.addSubEntry(db); } mProgress.setPercentComplete(100.0); mProgress.setVisible(false); mProgress.dispose(); in.close(); } catch (IOException e) { mProgress.setVisible(false); mProgress.dispose(); throw e; } }
public boolean checkSelectRank() { for (Key k : keys()) { if (k.compareTo(select(rank(k))) != 0) return false; } return true; }
/** @author gregsh */ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, Disposable { private static final Key<SimpleRendererComponent> MY_PANEL_PROPERTY = Key.create("EditorTextFieldCellRenderer.MyEditorPanel"); private final Project myProject; private final FileType myFileType; private final boolean myInheritFontFromLaF; protected EditorTextFieldCellRenderer( @Nullable Project project, @Nullable FileType fileType, @NotNull Disposable parent) { this(project, fileType, true, parent); } protected EditorTextFieldCellRenderer( @Nullable Project project, @Nullable FileType fileType, boolean inheritFontFromLaF, @NotNull Disposable parent) { myProject = project; myFileType = fileType; myInheritFontFromLaF = inheritFontFromLaF; Disposer.register(parent, this); } protected abstract String getText(JTable table, Object value, int row, int column); @Nullable protected TextAttributes getTextAttributes(JTable table, Object value, int row, int column) { return null; } @NotNull protected EditorColorsScheme getColorScheme(final JTable table) { return getEditorPanel(table).getEditor().getColorsScheme(); } protected void customizeEditor( @NotNull EditorEx editor, JTable table, Object value, boolean selected, int row, int column) { String text = getText(table, value, row, column); getEditorPanel(table).setText(text, getTextAttributes(table, value, row, column), selected); } @Override public Component getTableCellRendererComponent( JTable table, Object value, boolean selected, boolean focused, int row, int column) { RendererComponent panel = getEditorPanel(table); EditorEx editor = panel.getEditor(); editor.getColorsScheme().setEditorFontSize(table.getFont().getSize()); editor .getColorsScheme() .setColor(EditorColors.SELECTION_BACKGROUND_COLOR, table.getSelectionBackground()); editor .getColorsScheme() .setColor(EditorColors.SELECTION_FOREGROUND_COLOR, table.getSelectionForeground()); editor.setBackgroundColor(selected ? table.getSelectionBackground() : table.getBackground()); panel.setSelected(!Comparing.equal(editor.getBackgroundColor(), table.getBackground())); panel.setBorder( null); // prevents double border painting when ExtendedItemRendererComponentWrapper is used customizeEditor(editor, table, value, selected, row, column); return panel; } @NotNull private RendererComponent getEditorPanel(final JTable table) { RendererComponent panel = UIUtil.getClientProperty(table, MY_PANEL_PROPERTY); if (panel != null) { DelegateColorScheme scheme = (DelegateColorScheme) panel.getEditor().getColorsScheme(); scheme.setDelegate(EditorColorsManager.getInstance().getGlobalScheme()); return panel; } panel = createRendererComponent(myProject, myFileType, myInheritFontFromLaF); Disposer.register(this, panel); Disposer.register( this, new Disposable() { @Override public void dispose() { UIUtil.putClientProperty(table, MY_PANEL_PROPERTY, null); } }); table.putClientProperty(MY_PANEL_PROPERTY, panel); return panel; } @NotNull protected RendererComponent createRendererComponent( @Nullable Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { return new AbbreviatingRendererComponent(project, fileType, inheritFontFromLaF); } @Override public void dispose() {} public abstract static class RendererComponent extends CellRendererPanel implements Disposable { private final EditorEx myEditor; private final EditorTextField myTextField; protected TextAttributes myTextAttributes; private boolean mySelected; public RendererComponent( Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { Pair<EditorTextField, EditorEx> pair = createEditor(project, fileType, inheritFontFromLaF); myTextField = pair.first; myEditor = pair.second; add(myEditor.getContentComponent()); } public EditorEx getEditor() { return myEditor; } @NotNull private static Pair<EditorTextField, EditorEx> createEditor( Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { EditorTextField field = new EditorTextField(new MyDocument(), project, fileType, false, false); field.setSupplementary(true); field.setFontInheritedFromLAF(inheritFontFromLaF); field.addNotify(); // creates editor EditorEx editor = (EditorEx) ObjectUtils.assertNotNull(field.getEditor()); editor.setRendererMode(true); editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null)); editor.getSettings().setCaretRowShown(false); editor.getScrollPane().setBorder(null); return Pair.create(field, editor); } public void setText(String text, @Nullable TextAttributes textAttributes, boolean selected) { myTextAttributes = textAttributes; mySelected = selected; setText(text); } public abstract void setText(String text); @Override public void setBackground(Color bg) { // allows for striped tables if (myEditor != null) { myEditor.setBackgroundColor(bg); } super.setBackground(bg); } @Override public void dispose() { remove(myEditor.getContentComponent()); myTextField.removeNotify(); } protected void setTextToEditor(String text) { myEditor.getMarkupModel().removeAllHighlighters(); myEditor.getDocument().setText(text); ((EditorImpl) myEditor).resetSizes(); myEditor.getHighlighter().setText(text); if (myTextAttributes != null) { myEditor .getMarkupModel() .addRangeHighlighter( 0, myEditor.getDocument().getTextLength(), HighlighterLayer.ADDITIONAL_SYNTAX, myTextAttributes, HighlighterTargetArea.EXACT_RANGE); } ((EditorImpl) myEditor).setPaintSelection(mySelected); SelectionModel selectionModel = myEditor.getSelectionModel(); selectionModel.setSelection(0, mySelected ? myEditor.getDocument().getTextLength() : 0); } } public static class SimpleRendererComponent extends RendererComponent implements Disposable { public SimpleRendererComponent( Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { super(project, fileType, inheritFontFromLaF); } public void setText(String text) { setTextToEditor(text); } } public static class AbbreviatingRendererComponent extends RendererComponent { private static final char ABBREVIATION_SUFFIX = '\u2026'; // 2026 '...' private static final char RETURN_SYMBOL = '\u23ce'; private final StringBuilder myDocumentTextBuilder = new StringBuilder(); private Dimension myPreferredSize; private String myRawText; public AbbreviatingRendererComponent( Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { super(project, fileType, inheritFontFromLaF); } @Override public void setText(String text) { myRawText = text; myPreferredSize = null; } @Override public Dimension getPreferredSize() { if (myPreferredSize == null) { int maxLineLength = 0; int linesCount = 0; for (LineTokenizer lt = new LineTokenizer(myRawText); !lt.atEnd(); lt.advance()) { maxLineLength = Math.max(maxLineLength, lt.getLength()); linesCount++; } FontMetrics fontMetrics = ((EditorImpl) getEditor()) .getFontMetrics( myTextAttributes != null ? myTextAttributes.getFontType() : Font.PLAIN); int preferredHeight = getEditor().getLineHeight() * Math.max(1, linesCount); int preferredWidth = fontMetrics.charWidth('m') * maxLineLength; Insets insets = getInsets(); if (insets != null) { preferredHeight += insets.top + insets.bottom; preferredWidth += insets.left + insets.right; } myPreferredSize = new Dimension(preferredWidth, preferredHeight); } return myPreferredSize; } @Override protected void paintChildren(Graphics g) { updateText(g.getClipBounds()); super.paintChildren(g); } private void updateText(Rectangle clip) { FontMetrics fontMetrics = ((EditorImpl) getEditor()) .getFontMetrics( myTextAttributes != null ? myTextAttributes.getFontType() : Font.PLAIN); Insets insets = getInsets(); int maxLineWidth = getWidth() - (insets != null ? insets.left + insets.right : 0); myDocumentTextBuilder.setLength(0); boolean singleLineMode = getHeight() / (float) getEditor().getLineHeight() < 1.1f; if (singleLineMode) { appendAbbreviated( myDocumentTextBuilder, myRawText, 0, myRawText.length(), fontMetrics, maxLineWidth, true); } else { int lineHeight = getEditor().getLineHeight(); int firstVisibleLine = clip.y / lineHeight; float visibleLinesCountFractional = clip.height / (float) lineHeight; int linesToAppend = 1 + (int) visibleLinesCountFractional; LineTokenizer lt = new LineTokenizer(myRawText); for (int line = 0; !lt.atEnd() && line < firstVisibleLine; lt.advance(), line++) { myDocumentTextBuilder.append('\n'); } for (int line = 0; !lt.atEnd() && line < linesToAppend; lt.advance(), line++) { int start = lt.getOffset(); int end = start + lt.getLength(); appendAbbreviated( myDocumentTextBuilder, myRawText, start, end, fontMetrics, maxLineWidth, false); if (lt.getLineSeparatorLength() > 0) { myDocumentTextBuilder.append('\n'); } } } setTextToEditor(myDocumentTextBuilder.toString()); } private static void appendAbbreviated( StringBuilder to, String text, int start, int end, FontMetrics metrics, int maxWidth, boolean replaceLineTerminators) { int abbreviationLength = abbreviationLength(text, start, end, metrics, maxWidth, replaceLineTerminators); if (!replaceLineTerminators) { to.append(text, start, start + abbreviationLength); } else { CharSequenceSubSequence subSeq = new CharSequenceSubSequence(text, start, start + abbreviationLength); for (LineTokenizer lt = new LineTokenizer(subSeq); !lt.atEnd(); lt.advance()) { to.append(subSeq, lt.getOffset(), lt.getOffset() + lt.getLength()); if (lt.getLineSeparatorLength() > 0) { to.append(RETURN_SYMBOL); } } } if (abbreviationLength != end - start) { to.append(ABBREVIATION_SUFFIX); } } private static int abbreviationLength( String text, int start, int end, FontMetrics metrics, int maxWidth, boolean replaceSeparators) { if (metrics.charWidth('m') * (end - start) <= maxWidth) return end - start; int abbrWidth = metrics.charWidth(ABBREVIATION_SUFFIX); int abbrLength = 0; CharSequenceSubSequence subSeq = new CharSequenceSubSequence(text, start, end); for (LineTokenizer lt = new LineTokenizer(subSeq); !lt.atEnd(); lt.advance()) { for (int i = 0; i < lt.getLength(); i++, abbrLength++) { abbrWidth += metrics.charWidth(subSeq.charAt(lt.getOffset() + i)); if (abbrWidth >= maxWidth) return abbrLength; } if (replaceSeparators && lt.getLineSeparatorLength() != 0) { abbrWidth += metrics.charWidth(RETURN_SYMBOL); if (abbrWidth >= maxWidth) return abbrLength; abbrLength += lt.getLineSeparatorLength(); } } return abbrLength; } } private static class MyDocument extends UserDataHolderBase implements DocumentEx { RangeMarkerTree<RangeMarkerEx> myRangeMarkers = new RangeMarkerTree<RangeMarkerEx>(this) {}; char[] myChars = ArrayUtil.EMPTY_CHAR_ARRAY; String myString = ""; LineSet myLineSet = LineSet.createLineSet(myString); @Override public void setText(@NotNull CharSequence text) { String s = StringUtil.convertLineSeparators(text.toString()); myChars = new char[s.length()]; s.getChars(0, s.length(), myChars, 0); myString = new String(myChars); myLineSet = LineSet.createLineSet(myString); } @Override public void setStripTrailingSpacesEnabled(boolean isEnabled) {} @NotNull @Override public LineIterator createLineIterator() { return myLineSet.createIterator(); } @Override public void setModificationStamp(long modificationStamp) {} @Override public void addEditReadOnlyListener(@NotNull EditReadOnlyListener listener) {} @Override public void removeEditReadOnlyListener(@NotNull EditReadOnlyListener listener) {} @Override public void replaceText(@NotNull CharSequence chars, long newModificationStamp) {} @Override public void moveText(int srcStart, int srcEnd, int dstOffset) {} @Override public void suppressGuardedExceptions() {} @Override public void unSuppressGuardedExceptions() {} @Override public boolean isInEventsHandling() { return false; } @Override public void clearLineModificationFlags() {} @Override public boolean removeRangeMarker(@NotNull RangeMarkerEx rangeMarker) { return myRangeMarkers.removeInterval(rangeMarker); } @Override public void registerRangeMarker( @NotNull RangeMarkerEx rangeMarker, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) { myRangeMarkers.addInterval(rangeMarker, start, end, greedyToLeft, greedyToRight, layer); } @Override public boolean isInBulkUpdate() { return false; } @Override public void setInBulkUpdate(boolean value) {} @NotNull @Override public List<RangeMarker> getGuardedBlocks() { return Collections.emptyList(); } @Override public boolean processRangeMarkers(@NotNull Processor<? super RangeMarker> processor) { return myRangeMarkers.process(processor); } @Override public boolean processRangeMarkersOverlappingWith( int start, int end, @NotNull Processor<? super RangeMarker> processor) { return myRangeMarkers.processOverlappingWith(start, end, processor); } @NotNull @Override public String getText() { return myString; } @NotNull @Override public String getText(@NotNull TextRange range) { return range.substring(getText()); } @NotNull @Override public CharSequence getCharsSequence() { return myString; } @NotNull @Override public CharSequence getImmutableCharSequence() { return getText(); } @NotNull @Override public char[] getChars() { return myChars; } @Override public int getTextLength() { return myChars.length; } @Override public int getLineCount() { return myLineSet.findLineIndex(myChars.length) + 1; } @Override public int getLineNumber(int offset) { return myLineSet.findLineIndex(offset); } @Override public int getLineStartOffset(int line) { return myChars.length == 0 ? 0 : myLineSet.getLineStart(line); } @Override public int getLineEndOffset(int line) { return myChars.length == 0 ? 0 : myLineSet.getLineEnd(line); } @Override public void insertString(int offset, @NotNull CharSequence s) {} @Override public void deleteString(int startOffset, int endOffset) {} @Override public void replaceString(int startOffset, int endOffset, @NotNull CharSequence s) {} @Override public boolean isWritable() { return false; } @Override public long getModificationStamp() { return 0; } @Override public void fireReadOnlyModificationAttempt() {} @Override public void addDocumentListener(@NotNull DocumentListener listener) {} @Override public void addDocumentListener( @NotNull DocumentListener listener, @NotNull Disposable parentDisposable) {} @Override public void removeDocumentListener(@NotNull DocumentListener listener) {} @NotNull @Override public RangeMarker createRangeMarker(int startOffset, int endOffset) { return new RangeMarkerImpl(this, startOffset, endOffset, true) {}; } @NotNull @Override public RangeMarker createRangeMarker( int startOffset, int endOffset, boolean surviveOnExternalChange) { return null; } @Override public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {} @Override public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {} @Override public void setReadOnly(boolean isReadOnly) {} @NotNull @Override public RangeMarker createGuardedBlock(int startOffset, int endOffset) { return null; } @Override public void removeGuardedBlock(@NotNull RangeMarker block) {} @Nullable @Override public RangeMarker getOffsetGuard(int offset) { return null; } @Nullable @Override public RangeMarker getRangeGuard(int start, int end) { return null; } @Override public void startGuardedBlockChecking() {} @Override public void stopGuardedBlockChecking() {} @Override public void setCyclicBufferSize(int bufferSize) {} @NotNull @Override public RangeMarker createRangeMarker(@NotNull TextRange textRange) { return null; } @Override public int getLineSeparatorLength(int line) { return 0; } @Override public int getModificationSequence() { return 0; } } }