diff --git a/platform/util/src/com/intellij/util/io/IntToIntBtree.java b/platform/util/src/com/intellij/util/io/IntToIntBtree.java index bf9519a7c9ec..f918cf1e532b 100644 --- a/platform/util/src/com/intellij/util/io/IntToIntBtree.java +++ b/platform/util/src/com/intellij/util/io/IntToIntBtree.java @@ -17,11 +17,13 @@ abstract class IntToIntBtree { final BtreeIndexNodeView root; private int maxStepsSearched; private int pagesCount; - private int size; + private int count; + private int movedMembersCount; private final byte[] buffer; private boolean isLarge = true; private final ISimpleStorage storage; + private boolean offloadToSiblingsBeforeSplit = false; // TODO till effective insertion to page public IntToIntBtree(int _pageSize, int rootAddress, ISimpleStorage _storage, boolean initial) { pageSize = _pageSize; @@ -66,7 +68,7 @@ abstract class IntToIntBtree { int index = currentIndexNode.locate(key, true); if (index < 0) { - ++size; + ++count; currentIndexNode.insert(key, value, -index - 1); } else { currentIndexNode.setAddressAt(index, value); @@ -101,6 +103,18 @@ abstract class IntToIntBtree { this.pagesCount = pagesCount; } + public int getMovedMembersCount() { + return movedMembersCount; + } + + public void setMovedMembersCount(int movedMembersCount) { + this.movedMembersCount = movedMembersCount; + } + + void dumpStatistics() { + IOStatistics.dump("pagecount:" + pagesCount + ", height:" + maxStepsSearched + ", movedMembers:"+movedMembersCount); + } + void doFlush() { //pagesCache.clear(); } @@ -212,6 +226,7 @@ abstract class IntToIntBtree { static class BtreeIndexNodeView extends BtreePage { private boolean isIndexLeaf; private boolean isIndexLeafSet; + private static final int LARGE_MOVE_THRESHOLD = 5; BtreeIndexNodeView(IntToIntBtree btree) { super(btree); @@ -325,63 +340,8 @@ abstract class IntToIntBtree { if (parentAddress != 0) { parent = new BtreeIndexNodeView(btree); parent.setAddress(parentAddress); - int indexInParent = isIndexLeaf() ? parent.search(keyAt(0)) : -1; - BtreeIndexNodeView sibling = new BtreeIndexNodeView(btree); - - if (indexInParent > 0) { - if (doSanityCheck) { - myAssert(parent.keyAt(indexInParent) == keyAt(0)); - myAssert(parent.addressAt(indexInParent + 1) == -address); - } - - int siblingAddress = parent.addressAt(indexInParent); - sibling.setAddress(-siblingAddress); - - if (!sibling.isFull() && sibling.getChildrenCount() + 1 != sibling.getMaxChildrenCount()) { - if (doSanityCheck) { - sibling.dump("Offloading to left sibling"); - parent.dump("parent before"); - } - - sibling.insert(keyAt(0), addressAt(0), sibling.getChildrenCount()); - if (doSanityCheck) { - sibling.dump("Left sibling after"); - } - - parent.setKeyAt(indexInParent, keyAt(1)); - - int indexOflastChildToMove = getChildrenCount() - 1; - if (btree.isLarge) { - final int bytesToMove = indexOflastChildToMove * INTERIOR_SIZE; - getBytes(indexToOffset(1), btree.buffer, 0, bytesToMove); - putBytes(indexToOffset(0), btree.buffer, 0, bytesToMove); - } else { - for(int i = 0; i < indexOflastChildToMove; ++i) { - setAddressAt(i, addressAt(i + 1)); - setKeyAt(i, keyAt(i + 1)); - } - } - - setChildrenCount((short)indexOflastChildToMove); - } else if (indexInParent + 1 < parent.getChildrenCount()) { - insertToRightSibling(parent, indexInParent + 1, sibling); - } - // TODO: move members in non leaf level + handle cases below - } /*else if (indexInParent == -1) { - insertToRightSibling(parent, 0, sibling); - } else { - int a = 1; - }*/ - - if (!isFull()) { - sync(); - parent.sync(); - - if (doSanityCheck) { - dump("old node after split:"); - parent.dump("Parent node after split"); - } - return parentAddress; + if (btree.offloadToSiblingsBeforeSplit) { + if (doOffloadToSiblings(parent)) return parentAddress; } } @@ -448,7 +408,7 @@ abstract class IntToIntBtree { } int newRootAddress = btree.nextPage(); if (doSanityCheck) { - System.out.println("Pages:"+btree.pagesCount+", elements:"+btree.size + ", average:" + (btree.maxStepsSearched + 1)); + System.out.println("Pages:"+btree.pagesCount+", elements:"+btree.count + ", average:" + (btree.maxStepsSearched + 1)); } btree.setRootAddress(newRootAddress); parentAddress = newRootAddress; @@ -472,6 +432,70 @@ abstract class IntToIntBtree { return parentAddress; } + private boolean doOffloadToSiblings(BtreeIndexNodeView parent) { + int indexInParent = isIndexLeaf() ? parent.search(keyAt(0)) : -1; + BtreeIndexNodeView sibling = new BtreeIndexNodeView(btree); + + if (indexInParent > 0) { + if (doSanityCheck) { + myAssert(parent.keyAt(indexInParent) == keyAt(0)); + myAssert(parent.addressAt(indexInParent + 1) == -address); + } + + int siblingAddress = parent.addressAt(indexInParent); + sibling.setAddress(-siblingAddress); + + if (!sibling.isFull() && sibling.getChildrenCount() + 1 != sibling.getMaxChildrenCount()) { + if (doSanityCheck) { + sibling.dump("Offloading to left sibling"); + parent.dump("parent before"); + } + + sibling.insert(keyAt(0), addressAt(0), sibling.getChildrenCount()); + if (doSanityCheck) { + sibling.dump("Left sibling after"); + } + + parent.setKeyAt(indexInParent, keyAt(1)); + + int indexOflastChildToMove = getChildrenCount() - 1; + if (btree.isLarge) { + final int bytesToMove = indexOflastChildToMove * INTERIOR_SIZE; + getBytes(indexToOffset(1), btree.buffer, 0, bytesToMove); + putBytes(indexToOffset(0), btree.buffer, 0, bytesToMove); + } + else { + for (int i = 0; i < indexOflastChildToMove; ++i) { + setAddressAt(i, addressAt(i + 1)); + setKeyAt(i, keyAt(i + 1)); + } + } + + setChildrenCount((short)indexOflastChildToMove); + } + else if (indexInParent + 1 < parent.getChildrenCount()) { + insertToRightSibling(parent, indexInParent + 1, sibling); + } + // TODO: move members in non leaf level + handle cases below + } /*else if (indexInParent == -1) { + insertToRightSibling(parent, 0, sibling); + } else { + int a = 1; + }*/ + + if (!isFull()) { + sync(); + parent.sync(); + + if (doSanityCheck) { + dump("old node after split:"); + parent.dump("Parent node after split"); + } + return true; + } + return false; + } + private void insertToRightSibling(BtreeIndexNodeView parent, int indexInParent, BtreeIndexNodeView sibling) { int siblingAddress; siblingAddress = parent.addressAt(indexInParent + 1); @@ -553,10 +577,13 @@ abstract class IntToIntBtree { if (doSanityCheck) myAssert(recordCount < getMaxChildrenCount()); setChildrenCount((short)(recordCount + 1)); + final int itemsToMove = recordCount - index; + btree.movedMembersCount += itemsToMove; + // TODO Clever books tell us to use Btree for cheaper elements shifting within page if (isIndexLeaf()) { - if (btree.isLarge) { - final int bytesToMove = (recordCount - index) * INTERIOR_SIZE; + if (btree.isLarge && itemsToMove > LARGE_MOVE_THRESHOLD) { + final int bytesToMove = itemsToMove * INTERIOR_SIZE; getBytes(indexToOffset(index), btree.buffer, 0, bytesToMove); putBytes(indexToOffset(index + 1), btree.buffer, 0, bytesToMove); } else { @@ -571,7 +598,7 @@ abstract class IntToIntBtree { //
(
) {record_count - 1} // setAddressAt(recordCount + 1, addressAt(recordCount)); - if (btree.isLarge) { + if (btree.isLarge && itemsToMove > LARGE_MOVE_THRESHOLD) { int elementsAfterIndex = recordCount - index - 1; if (elementsAfterIndex > 0) { int bytesToMove = elementsAfterIndex * INTERIOR_SIZE; diff --git a/platform/util/src/com/intellij/util/io/MappedFileSimpleStorage.java b/platform/util/src/com/intellij/util/io/MappedFileSimpleStorage.java index bc512c0a0506..79dd723d2be8 100644 --- a/platform/util/src/com/intellij/util/io/MappedFileSimpleStorage.java +++ b/platform/util/src/com/intellij/util/io/MappedFileSimpleStorage.java @@ -13,9 +13,9 @@ final class MappedFileSimpleStorage implements ISimpleStorage { storage = new ResizeableMappedFile(file, initialSize, PersistentEnumeratorBase.ourLock); } - public MappedFileSimpleStorage(File file, int initialSize, int pageSize, float growFactor, boolean zeroPagesWhenExpand) throws IOException { + public MappedFileSimpleStorage(File file, int initialSize, int pageSize, boolean zeroPagesWhenExpand) throws IOException { FileUtil.createIfDoesntExist(file); - storage = new ResizeableMappedFile(file, initialSize, PersistentEnumeratorBase.ourLock, pageSize, growFactor, zeroPagesWhenExpand); + storage = new ResizeableMappedFile(file, initialSize, PersistentEnumeratorBase.ourLock, pageSize, zeroPagesWhenExpand); } @Override diff --git a/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java b/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java index 18ae5bbdbe11..181434e79560 100644 --- a/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java +++ b/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java @@ -45,7 +45,7 @@ public class PersistentBTreeEnumerator extends PersistentEnumeratorBase dataDescriptor, int initialSize) throws IOException { - super(file, new MappedFileSimpleStorage(file, initialSize, 1024 * 1024, 10f, false), dataDescriptor, initialSize); + super(file, new MappedFileSimpleStorage(file, initialSize, 1024 * 1024, false), dataDescriptor, initialSize); myInlineKeysNoMapping = myDataDescriptor instanceof InlineKeyDescriptor && !wantInlineKeyMapping(); myBuffer = new byte[getRecordSize()]; @@ -96,6 +96,7 @@ public class PersistentBTreeEnumerator extends PersistentEnumeratorBase extends PersistentEnumeratorBase extends PersistentEnumeratorBase implements Forceable, Closeable { } protected final void markDirty(boolean dirty) throws IOException { - if (dirty && isDirty()) return; + //assert Thread.holdsLock(this) || Thread.holdsLock(ourLock); // we hold one lock or another so can access myDirty + if (dirty && myDirty) return; synchronized (ourLock) { if (myDirty) { if (!dirty) { diff --git a/platform/util/src/com/intellij/util/io/ResizeableMappedFile.java b/platform/util/src/com/intellij/util/io/ResizeableMappedFile.java index 464e0d2e8f48..d4c85172ea91 100644 --- a/platform/util/src/com/intellij/util/io/ResizeableMappedFile.java +++ b/platform/util/src/com/intellij/util/io/ResizeableMappedFile.java @@ -29,9 +29,8 @@ public class ResizeableMappedFile implements Forceable { private long myLogicalSize; private final PagedFileStorage myStorage; - private final float myGrowFactor; - public ResizeableMappedFile(final File file, int initialSize, PagedFileStorage.StorageLock lock, int pageSize, float growFactor, boolean zeroWhenExpand) throws IOException { + public ResizeableMappedFile(final File file, int initialSize, PagedFileStorage.StorageLock lock, int pageSize, boolean zeroWhenExpand) throws IOException { myStorage = new PagedFileStorage(file, lock, pageSize, zeroWhenExpand); if (!file.exists() || file.length() == 0) { writeLength(0); @@ -43,13 +42,10 @@ public class ResizeableMappedFile implements Forceable { resize(initialSize); } } - - assert growFactor > 8f:"growFactor / 8 should be > 1"; - myGrowFactor = growFactor; } public ResizeableMappedFile(final File file, int initialSize, PagedFileStorage.StorageLock lock) throws IOException { - this(file, initialSize, lock, PagedFileStorage.DEFAULT_BUFFER_SIZE, 13f, true); + this(file, initialSize, lock, PagedFileStorage.DEFAULT_BUFFER_SIZE, true); } public long length() { @@ -78,7 +74,7 @@ public class ResizeableMappedFile implements Forceable { } private void expand() { - final long newSize = Math.min(Integer.MAX_VALUE, ((long)((realSize() + 1) * myGrowFactor)) >> 3); + final long newSize = Math.min(Integer.MAX_VALUE, ((realSize() + 1) * 13) >> 3); resize((int)newSize); } diff --git a/plugins/android/android.iml b/plugins/android/android.iml index 620b7ba080dc..b0125fcb44fc 100644 --- a/plugins/android/android.iml +++ b/plugins/android/android.iml @@ -31,20 +31,28 @@ + + + + - - - + + + + + + + diff --git a/plugins/android/lib/common.jar b/plugins/android/lib/common.jar index ed6a975e6fbd..006c6e499694 100644 Binary files a/plugins/android/lib/common.jar and b/plugins/android/lib/common.jar differ diff --git a/plugins/android/lib/ide_common.jar b/plugins/android/lib/ide_common.jar new file mode 100644 index 000000000000..ea5e33f2bfd8 Binary files /dev/null and b/plugins/android/lib/ide_common.jar differ diff --git a/plugins/android/lib/kxml2-2.3.0.jar b/plugins/android/lib/kxml2-2.3.0.jar new file mode 100644 index 000000000000..647095222a50 Binary files /dev/null and b/plugins/android/lib/kxml2-2.3.0.jar differ diff --git a/plugins/android/lib/layoutlib_api.jar b/plugins/android/lib/layoutlib_api.jar new file mode 100644 index 000000000000..4fa52af98744 Binary files /dev/null and b/plugins/android/lib/layoutlib_api.jar differ diff --git a/plugins/android/lib/ninepatch.jar b/plugins/android/lib/ninepatch.jar new file mode 100644 index 000000000000..a3ca418b94b7 Binary files /dev/null and b/plugins/android/lib/ninepatch.jar differ diff --git a/plugins/android/lib/sdklib.jar b/plugins/android/lib/sdklib.jar index f08303dbdd52..4c9d20a9fa35 100644 Binary files a/plugins/android/lib/sdklib.jar and b/plugins/android/lib/sdklib.jar differ diff --git a/plugins/android/lib/src/ide_common_src.zip b/plugins/android/lib/src/ide_common_src.zip new file mode 100644 index 000000000000..1341dc31da8e Binary files /dev/null and b/plugins/android/lib/src/ide_common_src.zip differ diff --git a/plugins/android/lib/src/layoutlib_api_src.zip b/plugins/android/lib/src/layoutlib_api_src.zip new file mode 100644 index 000000000000..c560c1db691b Binary files /dev/null and b/plugins/android/lib/src/layoutlib_api_src.zip differ diff --git a/plugins/android/lib/src/ninepatch_src.zip b/plugins/android/lib/src/ninepatch_src.zip new file mode 100644 index 000000000000..1cd3462ed541 Binary files /dev/null and b/plugins/android/lib/src/ninepatch_src.zip differ diff --git a/plugins/android/resources/icons/zoomActual.png b/plugins/android/resources/icons/zoomActual.png new file mode 100644 index 000000000000..7d0d91fa2979 Binary files /dev/null and b/plugins/android/resources/icons/zoomActual.png differ diff --git a/plugins/android/resources/icons/zoomFit.png b/plugins/android/resources/icons/zoomFit.png new file mode 100644 index 000000000000..f88a24a999cb Binary files /dev/null and b/plugins/android/resources/icons/zoomFit.png differ diff --git a/plugins/android/resources/icons/zoomIn.png b/plugins/android/resources/icons/zoomIn.png new file mode 100644 index 000000000000..e6a7db50be22 Binary files /dev/null and b/plugins/android/resources/icons/zoomIn.png differ diff --git a/plugins/android/resources/icons/zoomOut.png b/plugins/android/resources/icons/zoomOut.png new file mode 100644 index 000000000000..8f1a570d0b7a Binary files /dev/null and b/plugins/android/resources/icons/zoomOut.png differ diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index 7bfc8c26727b..186420d752e9 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -247,4 +247,14 @@ android.import.dependencies.import.module.task.title=Import library module {0} f android.import.dependencies.dialog.title=Import Android dependencies from property files android.import.dependencies.source.roots.dialog.title=Import source roots android.import.dependencies.source.roots.dialog.label=Choose source roots to add to your project +sdk.broken.error=Cannot parse Android SDK. Please reconfigure it in Android facet settings +android.layout.preview.cannot.load.library.error=Failed to load platform rendering library +android.layout.preview.cannot.load.resources=Cannot load module resources. +android.layout.preview.editor.name=Preview +android.layout.preview.tool.window.title=Preview +android.layout.preview.default.error.message=Cannot perform rendering +android.layout.preview.zoom.in.action.text=Zoom In +android.layout.preview.zoom.out.action.text=Zoom Out +android.layout.preview.zoom.actual.action.text=Zoom to actual size +android.layout.preview.zoom.to.fit.action.text=Zoom to Fit restart.adb.fix.text=Restart ADB service and try again \ No newline at end of file diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 3058a2ff9845..a195e93b613f 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -16,6 +16,10 @@ org.jetbrains.android.AndroidProjectComponent + + org.jetbrains.android.uipreview.AndroidLayoutPreviewToolWindowManager + + + + + + + + + + + + The "layout-devices" element is the root element of this schema. + + It must contain zero or more "device" elements that each define the configurations + available for a given device. + + These definitions are used in the Graphical Layout Editor in the + Android Development Tools (ADT) plugin for Eclipse. + + + + + + + + + + A device element must contain at most one "default" element + followed by one or more "config" elements. + + The "default" element defines all the default parameters + inherited by the following "config" elements. + Each "config" element can override the default values, if any. + + A "device" element also has a required "name" attribute that + represents the user-interface name of this device. + + + + + + + + + + + + + + + + + + + + + + + The parametersType define all the parameters that can happen either in a + "default" element or in a named "config" element. + Each parameter element can appear once at most. + + Parameters here are the same as those used to specify alternate Android + resources, as documented by + http://d.android.com/guide/topics/resources/resources-i18n.html#AlternateResources + + + + + + + + + + Specifies the configuration is for a particular Mobile Country Code. + + + + + + + + + + + + + + Specifies the configuration is for a particular Mobile Network Code. + + + + + + + + + + + + + + Specifies that the configuration is for a particular class of screen. + + + + + + + + + + + + + + + + Specifies that the configuration is for a taller/wider than traditional + screen. This is based purely on the aspect ration of the screen: QVGA, + HVGA, and VGA are notlong; WQVGA, WVGA, FWVGA are long. Note that long + may mean either wide or tall, depending on the current orientation. + + + + + + + + + + + + + + Specifies that the configuration is for a screen that is tall (port) or + wide (land). + + + + + + + + + + + + + + + Specifies the screen density the configuration is defined for. The medium + density of traditional HVGA screens (mdpi) is defined to be approximately + 160dpi; low density (ldpi) is 120, and high density (hdpi) is 240. There + is thus a 4:3 scaling factor between each density, so a 9x9 bitmap in ldpi + would be 12x12 is mdpi and 16x16 in hdpi. + The special nodpi density that can be used in resource qualifiers is not + a valid keyword here. + + + + + + + + + + + + + + + + + Specifies the touch type the configuration is defined for. + + + + + + + + + + + + + + + If your configuration uses a soft keyboard, use the keyssoft value. + If it doesn't and has a real keyboard, use keysexposed or keyshidden. + + + + + + + + + + + + + + + Specifies the primary text input method the configuration is designed for. + + + + + + + + + + + + + + + Specifies whether the primary non-touchscreen navigation control is + exposed or hidden. + + + + + + + + + + + + + + Specifies the primary non-touchscreen navigation method the configuration + is designed for. + + + + + + + + + + + + + + + + Specifies the device screen resolution, in pixels. + + + + + + + + + + + + + + + + + + + Specifies the actual density in X of the device screen. + + + + + + + + + + + + + Specifies the actual density in Y of the device screen. + + + + + + + + + + + + + + + + + The configType defines the content of a "config" element in a "device" element. + + A "config" element can have all the parameters elements defined by + "parameterType". It also has a required "name" attribute that indicates the + user-interface name for this configuration. + + + + + + + + + + + diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidSdkNotConfiguredException.java b/plugins/android/src/org/jetbrains/android/util/AndroidSdkNotConfiguredException.java new file mode 100644 index 000000000000..8d341d851490 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/util/AndroidSdkNotConfiguredException.java @@ -0,0 +1,7 @@ +package org.jetbrains.android.util; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidSdkNotConfiguredException extends Exception { +} diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java index 88b142170a1c..999bd76508e0 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java @@ -838,4 +838,43 @@ public class AndroidUtils { } return null; } + + public static int getIntAttrValue(@NotNull final XmlTag tag, @NotNull final String attrName) { + String value = ApplicationManager.getApplication().runReadAction(new Computable() { + public String compute() { + return tag.getAttributeValue(attrName, SdkConstants.NS_RESOURCES); + } + }); + try { + return Integer.parseInt(value); + } + catch (NumberFormatException e) { + return -1; + } + } + + @Nullable + public static XmlFile[] findXmlFiles(final Project project, final String... paths) { + XmlFile[] xmlFiles = new XmlFile[paths.length]; + for (int i = 0; i < paths.length; i++) { + String path = paths[i]; + final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(path); + PsiFile psiFile = file != null ? ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + public PsiFile compute() { + return PsiManager.getInstance(project).findFile(file); + } + }) : null; + if (psiFile == null) { + LOG.info("File " + path + " is not found"); + return null; + } + if (!(psiFile instanceof XmlFile)) { + LOG.info("File " + path + " is not an xml psiFile"); + return null; + } + xmlFiles[i] = (XmlFile)psiFile; + } + return xmlFiles; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcWatchedRootProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcWatchedRootProvider.java index b72e0276676c..d8e5dd45b76e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcWatchedRootProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcWatchedRootProvider.java @@ -11,6 +11,7 @@ import com.intellij.openapi.roots.WatchedRootsProvider; import org.jetbrains.annotations.NotNull; import java.io.File; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -26,12 +27,16 @@ public class MvcWatchedRootProvider implements WatchedRootsProvider { @NotNull public Set getRootsToWatch() { - final Set result = new HashSet(); + if (!myProject.isInitialized()) return Collections.emptySet(); + + Set result = null; for (Module module : ModuleManager.getInstance(myProject).getModules()) { final MvcFramework framework = MvcModuleStructureSynchronizer.getFramework(module); if (framework == null) continue; + if (result == null) result = new HashSet(); + final File sdkWorkDir = framework.getCommonPluginsDir(module); if (sdkWorkDir != null) { result.add(sdkWorkDir.getAbsolutePath()); @@ -43,7 +48,7 @@ public class MvcWatchedRootProvider implements WatchedRootsProvider { } } - return result; + return result == null ? Collections.emptySet() : result; } }