You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@netbeans.apache.org by GitBox <gi...@apache.org> on 2017/11/20 09:49:35 UTC

[GitHub] geertjanw closed pull request #283: Integrating NetBeans after donation changes.

geertjanw closed pull request #283: Integrating NetBeans after donation changes.
URL: https://github.com/apache/incubator-netbeans/pull/283
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/api.java.classpath/src/org/netbeans/api/java/classpath/ClassPath.java b/api.java.classpath/src/org/netbeans/api/java/classpath/ClassPath.java
index 0e7507493..ff10fc597 100644
--- a/api.java.classpath/src/org/netbeans/api/java/classpath/ClassPath.java
+++ b/api.java.classpath/src/org/netbeans/api/java/classpath/ClassPath.java
@@ -26,7 +26,6 @@
 import java.io.IOException;
 import java.lang.ref.Reference;
 import java.lang.ref.SoftReference;
-import java.lang.ref.WeakReference;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -58,9 +57,7 @@
 import org.netbeans.spi.java.classpath.FlaggedClassPathImplementation;
 import org.netbeans.spi.java.classpath.PathResourceImplementation;
 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
-import org.openide.filesystems.FileAttributeEvent;
 import org.openide.filesystems.FileChangeAdapter;
-import org.openide.filesystems.FileChangeListener;
 import org.openide.filesystems.FileEvent;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileRenameEvent;
@@ -68,6 +65,7 @@
 import org.openide.filesystems.URLMapper;
 import org.openide.util.BaseUtilities;
 import org.openide.util.Lookup;
+import org.openide.util.Pair;
 import org.openide.util.Parameters;
 import org.openide.util.WeakListeners;
 
@@ -92,6 +90,7 @@
  * (compiler, debugger, executor). Names are not limited to the listed ones; an extension
  * module might add its own private classpath type.
  */
+
 public final class ClassPath {
 
     static  {
@@ -246,33 +245,73 @@ public ClassPathImplementation getClassPathImpl(ClassPath cp) {
             }
             current = this.invalidRoots;
         }
-        List<ClassPath.Entry> entries = this.entries();
-        FileObject[] ret;
+        final List<ClassPath.Entry> entries = this.entries();
+        final List<Pair<ClassPath.Entry,Pair<FileObject,File>>> rootPairs = createRoots(entries);
+        FileObject[] roots = rootPairs.stream()
+                            .map((p) -> p.second().first())
+                            .filter((fo) -> fo != null)
+                            .toArray((size) -> new FileObject[size]);
         synchronized (this) {
             if (this.invalidRoots == current) {
                 if (rootsCache == null || rootsListener == null) {
                     attachRootsListener();
-                    this.rootsCache = createRoots (entries);
+                    listenOnRoots(rootPairs);
+                    this.rootsCache = roots;
+                } else {
+                    roots = this.rootsCache;
+                }
+            }
+        }
+        return roots;
+    }
+
+    private File getFile(final ClassPath.Entry entry) {
+        URL url = entry.getURL();
+        if ("jar".equals(url.getProtocol())) { //NOI18N
+            url = FileUtil.getArchiveFile(url);
+            if (url == null) {
+                LOG.log(
+                    Level.WARNING,
+                    "Invalid classpath root: {0} provided by: {1}", //NOI18N
+                    new Object[]{
+                        entry.getURL(),
+                        impl
+                    });
+                return null;
+            }
+        }
+        if (!"file".equals(url.getProtocol())) { // NOI18N
+            // Try to convert nbinst to file
+            FileObject fileFo = URLMapper.findFileObject(url);
+            if (fileFo != null) {
+                URL external = URLMapper.findURL(fileFo, URLMapper.EXTERNAL);
+                if (external != null) {
+                    url = external;
                 }
-                ret = this.rootsCache;
             }
-            else {
-                ret = createRoots (entries);
+        }
+        try {
+            //todo: Ignore non file urls, we can try to url->fileobject->url
+            //if it becomes a file.
+            if ("file".equals(url.getProtocol())) { //NOI18N
+                return FileUtil.normalizeFile(BaseUtilities.toFile(url.toURI()));
             }
+        } catch (IllegalArgumentException e) {
+            LOG.log(Level.WARNING, "Unexpected URL <{0}>: {1}", new Object[] {url, e});
+            //pass
+        } catch (URISyntaxException e) {
+            LOG.log(Level.WARNING, "Invalid URL: {0}", url);
+            //pass
         }
-        assert ret != null;
-        return ret;
+        return null;
     }
 
-    private FileObject[] createRoots (final List<ClassPath.Entry> entries) {
-        final List<FileObject> l = new ArrayList<FileObject> ();
-        final Set<File> listenOn = new HashSet<File>();
+    private List<Pair<ClassPath.Entry,Pair<FileObject,File>>> createRoots (final List<ClassPath.Entry> entries) {
+        final List<Pair<ClassPath.Entry,Pair<FileObject,File>>> l = new ArrayList<> ();
         for (Entry entry : entries) {
-            FileObject fo = entry.getRoot();
             File file = null;
+            FileObject fo = entry.getRoot();
             if (fo != null) {
-                l.add(fo);
-                root2Filter.put(fo, entry.filter);
                 FileObject fileFo = FileUtil.getArchiveFile(fo);
                 if (fileFo  == null) {
                     fileFo = fo;
@@ -280,44 +319,22 @@ public ClassPathImplementation getClassPathImpl(ClassPath cp) {
                 file = FileUtil.toFile(fileFo);
             }
             if (file == null) {
-                URL url = entry.getURL();
-                if ("jar".equals(url.getProtocol())) { //NOI18N
-                    url = FileUtil.getArchiveFile(url);
-                    if (url == null) {
-                        LOG.log(
-                            Level.WARNING,
-                            "Invalid classpath root: {0} provided by: {1}", //NOI18N
-                            new Object[]{
-                                entry.getURL(),
-                                impl
-                            });
-                        continue;
-                    }
-                }
-                if (!"file".equals(url.getProtocol())) { // NOI18N
-                    // Try to convert nbinst to file
-                    FileObject fileFo = URLMapper.findFileObject(url);
-                    if (fileFo != null) {
-                        URL external = URLMapper.findURL(fileFo, URLMapper.EXTERNAL);
-                        if (external != null) {
-                            url = external;
-                        }
-                    }
-                }
-                try {
-                    //todo: Ignore non file urls, we can try to url->fileobject->url
-                    //if it becomes a file.
-                    if ("file".equals(url.getProtocol())) { //NOI18N
-                        file = FileUtil.normalizeFile(BaseUtilities.toFile(url.toURI()));
-                    }
-                } catch (IllegalArgumentException e) {
-                    LOG.log(Level.WARNING, "Unexpected URL <{0}>: {1}", new Object[] {url, e});
-                    //pass
-                } catch (URISyntaxException e) {
-                    LOG.log(Level.WARNING, "Invalid URL: {0}", url);
-                    //pass
-                }
+                file = getFile(entry);
+            }
+            l.add(Pair.of(entry,Pair.of(fo,file)));
+        }
+        return l;
+    }
+
+    private void listenOnRoots (final List<Pair<ClassPath.Entry,Pair<FileObject,File>>> roots) {
+        final Set<File> listenOn = new HashSet<>();
+        for (Pair<ClassPath.Entry,Pair<FileObject,File>> p : roots) {
+            final ClassPath.Entry entry = p.first();
+            final FileObject fo = p.second().first();
+            if (fo != null) {
+                root2Filter.put(fo, entry.filter);
             }
+            final File file = p.second().second();
             if (file != null) {
                 listenOn.add(file);
             }
@@ -326,7 +343,6 @@ public ClassPathImplementation getClassPathImpl(ClassPath cp) {
         if (rL != null) {
             rL.addRoots (listenOn);
         }
-        return l.toArray (new FileObject[l.size()]);
     }
 
     /**
diff --git a/java.api.common/manifest.mf b/java.api.common/manifest.mf
index 61e8304be..46a3ada5a 100644
--- a/java.api.common/manifest.mf
+++ b/java.api.common/manifest.mf
@@ -1,4 +1,4 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.modules.java.api.common/0
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/api/common/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.120
+OpenIDE-Module-Specification-Version: 1.123
diff --git a/java.api.common/nbproject/project.xml b/java.api.common/nbproject/project.xml
index fc47e033e..2a7aea84a 100644
--- a/java.api.common/nbproject/project.xml
+++ b/java.api.common/nbproject/project.xml
@@ -102,6 +102,14 @@
                         <specification-version>8.19</specification-version>
                     </run-dependency>
                 </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.autoupdate.services</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.53</specification-version>
+                    </run-dependency>
+                </dependency>
                 <dependency>
                     <code-name-base>org.netbeans.modules.editor.util</code-name-base>
                     <build-prerequisite/>
@@ -478,6 +486,7 @@
                 <package>org.netbeans.modules.java.api.common.ant</package>
                 <package>org.netbeans.modules.java.api.common.applet</package>
                 <package>org.netbeans.modules.java.api.common.classpath</package>
+                <package>org.netbeans.modules.java.api.common.problems</package>
                 <package>org.netbeans.modules.java.api.common.project</package>
                 <package>org.netbeans.modules.java.api.common.project.ui</package>
                 <package>org.netbeans.modules.java.api.common.project.ui.customizer</package>
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/impl/TemplateModuleDeclarator.java b/java.api.common/src/org/netbeans/modules/java/api/common/impl/TemplateModuleDeclarator.java
index 0b41f948a..18b854899 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/impl/TemplateModuleDeclarator.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/impl/TemplateModuleDeclarator.java
@@ -190,9 +190,10 @@ private void findModuleNames(String fqn, ClassPath searchPath, Set<String> modul
             }
         }
         if (classResource != null) {
-            FileObject r = searchPath.findOwnerRoot(classResource);
+            final FileObject r = searchPath.findOwnerRoot(classResource);
             for (ClassPath.Entry e : searchPath.entries()) {
-                if (e.getRoot().equals(r)) {
+                final FileObject er = e.getRoot();
+                if (er != null && er.equals(r)) {
                     String name = SourceUtils.getModuleName(e.getURL(), true);
                     moduleNames.add(name);
                 }
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/problems/Bundle.properties b/java.api.common/src/org/netbeans/modules/java/api/common/problems/Bundle.properties
new file mode 100644
index 000000000..e7d1d0916
--- /dev/null
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/problems/Bundle.properties
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+WARN_MissingRequiredModule=Missing required module
+DESC_MissingRequiredModule=The project {0} requires a module {1} which is not installed.
+TXT_ModuleListRefresh=Refreshing module list
+TXT_InstallModule=Installing module {0}
+ERR_NoSuchModule=Cannot find module {0}.
+ERR_FailedToInstallModule=Failed to install module {0}.
+TXT_RestartConfirm=<html>Module installation requires IDE restart.<br>Restart now?
+TITLE_Restart=Install Module
+TXT_Restart=Restarting
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/problems/MissingModuleProblemsProvider.java b/java.api.common/src/org/netbeans/modules/java/api/common/problems/MissingModuleProblemsProvider.java
new file mode 100644
index 000000000..46eb1f1bb
--- /dev/null
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/problems/MissingModuleProblemsProvider.java
@@ -0,0 +1,370 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.api.common.problems;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Logger;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.SwingUtilities;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.autoupdate.InstallSupport;
+import org.netbeans.api.autoupdate.OperationContainer;
+import org.netbeans.api.autoupdate.OperationException;
+import org.netbeans.api.autoupdate.OperationSupport.Restarter;
+import org.netbeans.api.autoupdate.UpdateElement;
+import org.netbeans.api.autoupdate.UpdateManager;
+import org.netbeans.api.autoupdate.UpdateUnit;
+import org.netbeans.api.autoupdate.UpdateUnitProvider;
+import org.netbeans.api.autoupdate.UpdateUnitProviderFactory;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.spi.project.ui.ProjectProblemResolver;
+import org.netbeans.spi.project.ui.ProjectProblemsProvider;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.modules.ModuleInfo;
+import org.openide.modules.Modules;
+import org.openide.modules.SpecificationVersion;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+import org.openide.util.NbBundle;
+import org.openide.util.Pair;
+import org.openide.util.Parameters;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+final class MissingModuleProblemsProvider implements ProjectProblemsProvider, PreferenceChangeListener, LookupListener {
+
+    private static final Logger LOG = Logger.getLogger(MissingModuleProblemsProvider.class.getName());
+    private static final String PATTERN_REQ_MODULE = "requiredModule-";    //NOI18N
+
+    private static final AtomicBoolean catalogRefreshed = new AtomicBoolean();
+
+    private final PropertyChangeSupport listeners;
+    private final AtomicReference<Collection<ProjectProblem>> cache;
+    private final AtomicReference<Preferences> prefsCache;
+    private final AtomicReference<Lookup.Result<ModuleInfo>> modulesResult;
+    private final Project project;
+
+    MissingModuleProblemsProvider(@NonNull final Project project) {
+        Parameters.notNull("project", project); //NOI18N
+        this.project = project;
+        this.listeners = new PropertyChangeSupport(this);
+        this.cache = new AtomicReference<>();
+        this.prefsCache = new AtomicReference<>();
+        this.modulesResult = new AtomicReference<>();
+    }
+
+    @Override
+    public Collection<? extends ProjectProblem> getProblems() {
+        Collection<ProjectProblem> problems = cache.get();
+        if (problems == null) {
+            try {
+                listenOnModules();
+                final Preferences prefs = getPreferences();
+                problems = Arrays.stream(prefs.keys())
+                        .filter((n) -> n.startsWith(PATTERN_REQ_MODULE))
+                        .map((n) -> prefs.get(n, PROP_PROBLEMS))
+                        .filter((mn) -> mn != null)
+                        .filter(MissingModuleProblemsProvider::notInstalled)
+                        .map((modName) -> ProjectProblem.createWarning(
+                            NbBundle.getMessage(MissingModuleProblemsProvider.class, "WARN_MissingRequiredModule"),
+                            NbBundle.getMessage(MissingModuleProblemsProvider.class, "DESC_MissingRequiredModule",
+                                    ProjectUtils.getInformation(project).getDisplayName(),
+                                    modName),
+                            new ReqModuleProblem(modName)))
+                        .collect(Collectors.toList());
+            } catch (BackingStoreException e) {
+                Exceptions.printStackTrace(e);
+                problems = Collections.emptySet();
+            }
+            if (!cache.compareAndSet(null, problems)) {
+                final Collection<ProjectProblem> current = cache.get();
+                if (current != null) {
+                    problems = current;
+                }
+            }
+        }
+        return problems;
+    }
+
+    @Override
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        Parameters.notNull("listener", listener);   //NOI18N
+        this.listeners.addPropertyChangeListener(listener);
+    }
+
+    @Override
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        Parameters.notNull("listener", listener);
+        this.listeners.removePropertyChangeListener(listener);
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        problemsChanged();
+    }
+
+    @Override
+    public void resultChanged(LookupEvent ev) {
+        problemsChanged();
+    }
+
+    private void problemsChanged() {
+        this.cache.set(null);
+        this.listeners.firePropertyChange(PROP_PROBLEMS, null, null);
+    }
+
+    private void listenOnModules() {
+        Lookup.Result<ModuleInfo> modules = modulesResult.get();
+        if (modules == null) {
+            modules = Lookup.getDefault().lookupResult(ModuleInfo.class);
+            if (modulesResult.compareAndSet(null, modules)) {
+                modules.addLookupListener(WeakListeners.create(LookupListener.class, this, modules));
+            }
+        }
+    }
+
+    private Preferences getPreferences() {
+        Preferences prefs = prefsCache.get();
+        if (prefs == null) {
+            prefs = ProjectUtils.getPreferences(project, MissingModuleProblemsProvider.class, true);
+            if (prefsCache.compareAndSet(null, prefs)) {
+                prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+            }
+        }
+        return prefs;
+    }
+
+    private static boolean notInstalled(@NonNull final String cnb) {
+        final ModuleInfo module = Modules.getDefault().findCodeNameBase(cnb);
+        return module == null;
+    }
+
+    private static void refreshModuleList() {
+        if (catalogRefreshed.compareAndSet(false, true)) {
+            final ProgressHandle refreshHandle = ProgressHandle.createHandle(NbBundle.getMessage(
+                    MissingModuleProblemsProvider.class,
+                    "TXT_ModuleListRefresh"));
+            refreshHandle.start();
+            try {
+                for (UpdateUnitProvider provider : UpdateUnitProviderFactory.getDefault().getUpdateUnitProviders(false)) {
+                    try {
+                        provider.refresh(refreshHandle, true);
+                    } catch (IOException ex) {
+                        Exceptions.printStackTrace(ex);
+                    }
+                }
+            } finally {
+                refreshHandle.finish();
+            }
+        }
+    }
+
+    @CheckForNull
+    private static UpdateUnit findUpdateUnit(@NonNull final String moduleCNB) {
+        for (UpdateUnit updateUnit : UpdateManager.getDefault().getUpdateUnits()) {
+             final String codeName = updateUnit.getCodeName();
+             if (moduleCNB.equals(codeName)) {
+                 return updateUnit;
+            }
+        }
+        return null;
+    }
+
+    private static InstallationResult installUpdate(@NonNull final UpdateElement update) {
+        final ProgressHandle installHandle = ProgressHandle.createHandle(NbBundle.getMessage(
+                MissingModuleProblemsProvider.class,
+                "TXT_InstallModule",
+                update.getDisplayName()));
+        installHandle.start();
+        try {
+            final OperationContainer<InstallSupport> container = OperationContainer.createForInstall();
+            container.add(Collections.singleton(update));
+            final InstallSupport support = container.getSupport();
+            try {
+                final InstallSupport.Validator v = support.doDownload(installHandle, true, true);
+                final InstallSupport.Installer i = support.doValidate(v, installHandle);
+                final Restarter r = support.doInstall(i, installHandle);
+                return InstallationResult.success(support, r);
+            } catch (OperationException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        } finally {
+            installHandle.finish();
+        }
+        return InstallationResult.failure();
+    }
+
+    private static final class InstallationResult {
+        private final boolean success;
+        private final InstallSupport support;
+        private final Restarter restarter;
+
+        private InstallationResult(
+                final boolean success,
+                final InstallSupport support,
+                final Restarter restarter) {
+            this.success = success;
+            this.support = support;
+            this.restarter = restarter;
+        }
+
+        boolean isSuccess() {
+            return success;
+        }
+
+        Optional<Pair<InstallSupport,Restarter>> getRestarter() {
+            if (!isSuccess()) {
+                throw new IllegalStateException("Failed installation.");
+            }
+            return Optional.ofNullable(restarter)
+                    .map((r) -> Pair.of(support, r));
+        }
+
+        static InstallationResult success(
+                @NonNull final InstallSupport support,
+                @NullAllowed final Restarter restarter) {
+            Parameters.notNull("support", support);
+            return new InstallationResult(true, support, restarter);
+        }
+
+        static InstallationResult failure() {
+            return new InstallationResult(false, null, null);
+        }
+    }
+
+    private static final class ReqModuleProblem implements ProjectProblemResolver {
+        private static final RequestProcessor WORKER = new RequestProcessor(ReqModuleProblem.class);
+
+        private final String moduleCNB;
+
+        ReqModuleProblem(@NonNull final String moduleCNB) {
+            Parameters.notNull("moduleCNB", moduleCNB);   //NOI18N
+            this.moduleCNB = moduleCNB;
+        }
+
+        @Override
+        public Future<Result> resolve() {
+            return WORKER.submit(() -> {
+                refreshModuleList();
+                final UpdateUnit moduleToInstall = findUpdateUnit(moduleCNB);
+                if (moduleToInstall != null) {
+                    final UpdateElement update = findLatest(moduleToInstall.getAvailableUpdates());
+                    if (update != null) {
+                        final InstallationResult instRes = installUpdate(update);
+                        if (instRes.isSuccess()) {
+                            instRes.getRestarter().ifPresent((p) -> {
+                                maybeRestart(p.first(), p.second());
+                            });
+                            return Result.create(Status.RESOLVED);
+                        } else {
+                            showError(NbBundle.getMessage(
+                            MissingModuleProblemsProvider.class,
+                            "ERR_FailedToInstallModule",
+                            moduleCNB));
+                        }
+                    }
+                } else {
+                    showError(NbBundle.getMessage(
+                            MissingModuleProblemsProvider.class,
+                            "ERR_NoSuchModule",
+                            moduleCNB));
+                }
+                return Result.create(Status.UNRESOLVED);
+            });
+        }
+
+        private static UpdateElement findLatest(List<? extends UpdateElement> updates) {
+            UpdateElement newest = null;
+            for (UpdateElement ue : updates) {
+                if (newest == null || compare(newest, ue) < 0) {
+                    newest = ue;
+                }
+            }
+            return newest;
+        }
+
+        private static int compare(UpdateElement u1, UpdateElement u2) {
+            return new SpecificationVersion(u1.getSpecificationVersion()).compareTo(
+                    new SpecificationVersion(u2.getSpecificationVersion()));
+        }
+
+        private static void showError(@NonNull final String message) {
+            SwingUtilities.invokeLater(() -> {
+                DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
+                        message,
+                        NotifyDescriptor.Message.ERROR_MESSAGE));
+            });
+        }
+
+        private static void maybeRestart(
+                @NonNull final InstallSupport is,
+                @NonNull final Restarter r) {
+            SwingUtilities.invokeLater(()-> {
+                final Object option = DialogDisplayer.getDefault().notify(new DialogDescriptor.Message.Confirmation(
+                        NbBundle.getMessage(MissingModuleProblemsProvider.class, "TXT_RestartConfirm"),
+                        NbBundle.getMessage(MissingModuleProblemsProvider.class, "TITLE_Restart"),
+                        DialogDescriptor.YES_NO_OPTION));
+                if (option == DialogDescriptor.YES_OPTION) {
+                    final ProgressHandle handle = ProgressHandle.createHandle(NbBundle.getMessage(
+                            MissingModuleProblemsProvider.class,
+                            "TXT_Restart"));
+                    handle.start();
+                    try {
+                        try {
+                            is.doRestart(r, handle);
+                        } catch (OperationException e) {
+                            //Cancelled
+                            is.doRestartLater(r);
+                        }
+                    } finally {
+                        handle.finish();
+                    }
+                } else {
+                    is.doRestartLater(r);
+                }
+            });
+        }
+    }
+}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/problems/ProjectProblemsProviders.java b/java.api.common/src/org/netbeans/modules/java/api/common/problems/ProjectProblemsProviders.java
new file mode 100644
index 000000000..1085a8529
--- /dev/null
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/problems/ProjectProblemsProviders.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.api.common.problems;
+
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.project.Project;
+import org.netbeans.spi.project.ui.ProjectProblemsProvider;
+
+/**
+ * Support for {@link ProjectProblemsProvider}.
+ * @author Tomas Zezula
+ * @since 1.123
+ */
+public final class ProjectProblemsProviders {
+
+    private ProjectProblemsProviders() {
+        throw new IllegalStateException("No instance allowed");
+    }
+
+    @NonNull
+    public static ProjectProblemsProvider createMissingModuleProjectProblemsProvider(@NonNull final Project project) {
+        return new MissingModuleProblemsProvider(project);
+    }
+}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ProjectProperties.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ProjectProperties.java
index b9c96faaf..003f99f59 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/ProjectProperties.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ProjectProperties.java
@@ -389,4 +389,7 @@
 
     //NB 6.1 tracking of files modifications
     public static final String TRACK_FILE_CHANGES="track.file.changes"; //NOI18N
+
+    /** @since 1.122*/
+    public static final String MANIFEST_FILE="manifest.file";   //NOI18N
 }
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/Bundle.properties b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/Bundle.properties
index 5500d45d4..4c9aaf7d3 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/Bundle.properties
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/Bundle.properties
@@ -60,6 +60,7 @@ TP_CompileOnSaveDisabled=Compile on Save is disabled. It can be enabled in Proje
 #{0} - folder name
 TXT_PasteInto=Paste into {0}
 CTL_TestLibrariesNode=Test Libraries
+MultiModuleNodeFactory.gensrc=Generated Sources ({0})
 
 #ModuleNode
 #{0} - module name
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/JavaSourceNodeFactory.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/JavaSourceNodeFactory.java
index ff732baa7..5e772c4cc 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/JavaSourceNodeFactory.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/JavaSourceNodeFactory.java
@@ -198,7 +198,7 @@ public void removeChangeListener(ChangeListener l) {
         
         @Override
         public Node node(SourceGroupKey key) {
-            return new PackageViewFilterNode(key, project);
+            return new PackageViewFilterNode(key.group, project, !key.trueSource);
         }
         
         @Override
@@ -319,90 +319,4 @@ public void addPropertyChangeListener(PropertyChangeListener listener) {}
         public void removePropertyChangeListener(PropertyChangeListener listener) {}
 
     }
-
-    /**
-     * Adjusts some display characteristics of source group root node.
-     */
-    private static class PackageViewFilterNode extends FilterNode {
-        
-        private final String nodeName;
-        private final Project project;
-        private final boolean trueSource;
-        
-        public PackageViewFilterNode(SourceGroupKey sourceGroupKey, Project project) {
-            super(PackageView.createPackageView(sourceGroupKey.group));
-            this.project = project;
-            this.nodeName = "Sources";
-            trueSource = sourceGroupKey.trueSource;
-        }
-        
-        public @Override Action[] getActions(boolean context) {
-            List<Action> actions = new ArrayList<Action>(Arrays.asList(super.getActions(context)));
-            if (trueSource) {
-                actions.add(null);
-                actions.add(new PreselectPropertiesAction(project, nodeName));
-            } else {
-                // Just take out "New File..." as this would be misleading.
-                Iterator<Action> scan = actions.iterator();
-                while (scan.hasNext()) {
-                    Action a = scan.next();
-                    if (a != null && a.getClass().getName().equals("org.netbeans.modules.project.ui.actions.NewFile$WithSubMenu")) { // NOI18N
-                        scan.remove();
-                    }
-                }
-            }
-            return actions.toArray(new Action[actions.size()]);
-        }
-
-        public @Override String getHtmlDisplayName() {
-            if (trueSource) {
-                return super.getHtmlDisplayName();
-            }
-            String htmlName = getOriginal().getHtmlDisplayName();
-            if (htmlName == null) {
-                try {
-                    htmlName = XMLUtil.toElementContent(super.getDisplayName());
-                } catch (CharConversionException x) {
-                    return null; // never mind
-                }
-            }
-            return "<font color='!controlShadow'>" + htmlName + "</font>"; // NOI18N
-        }
-        
-    }
-    
-    
-    /** The special properties action
-     */
-    static class PreselectPropertiesAction extends AbstractAction {
-        
-        private final Project project;
-        private final String nodeName;
-        private final String panelName;
-        
-        public PreselectPropertiesAction(Project project, String nodeName) {
-            this(project, nodeName, null);
-        }
-        
-        public PreselectPropertiesAction(Project project, String nodeName, String panelName) {
-            super(NbBundle.getMessage(JavaSourceNodeFactory.class, "LBL_Properties_Action")); //NOI18N
-            this.project = project;
-            this.nodeName = nodeName;
-            this.panelName = panelName;
-        }
-        
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            CustomizerProvider2 cp2 = project.getLookup().lookup(CustomizerProvider2.class);
-            if (cp2 != null) {
-                cp2.showCustomizer(nodeName, panelName);
-            } else {
-                CustomizerProvider cp = project.getLookup().lookup(CustomizerProvider.class);
-                if (cp != null) {
-                    cp.showCustomizer();
-                }
-            }            
-        }
-    }
-    
 }
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/MultiModuleNodeFactory.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/MultiModuleNodeFactory.java
index 4406c7b53..473b3e8d9 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/MultiModuleNodeFactory.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/MultiModuleNodeFactory.java
@@ -22,6 +22,7 @@
 import java.awt.datatransfer.Transferable;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -32,14 +33,17 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.swing.Action;
+import javax.swing.Icon;
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.annotations.common.CheckForNull;
 import org.netbeans.api.annotations.common.NonNull;
@@ -68,6 +72,7 @@
 import org.openide.actions.PasteAction;
 import org.openide.actions.ToolsAction;
 import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeAdapter;
 import org.openide.filesystems.FileChangeListener;
 import org.openide.filesystems.FileEvent;
 import org.openide.filesystems.FileObject;
@@ -105,28 +110,33 @@
 /**
  * Multi Module logical view content.
  * @author Tomas Zezula
- * @since 1.115
+ * @since 1.121
  */
 public final class MultiModuleNodeFactory implements NodeFactory {
     private static final Logger LOG = Logger.getLogger(MultiModuleNodeFactory.class.getName());
     private static final RequestProcessor RP = new RequestProcessor(MultiModuleNodeFactory.class);
     private final MultiModule sourceModules;
     private final MultiModule testModules;
+    private final ProcessorGeneratedSources procGenSrc;
     private final LibrariesSupport libsSupport;
 
     private MultiModuleNodeFactory(
             @NullAllowed final MultiModule sourceModules,
             @NullAllowed final MultiModule testModules,
+            @NonNull final ProcessorGeneratedSources procGenSrc,
             @NullAllowed final LibrariesSupport libsSupport) {
+        Parameters.notNull("procGenSrc", procGenSrc);   //NOI18
         this.sourceModules = sourceModules;
         this.testModules = testModules;
         this.libsSupport = libsSupport;
+        this.procGenSrc = procGenSrc;
     }
 
     @Override
     public NodeList<?> createNodes(@NonNull final Project project) {
         return new Nodes(
                 project,
+                procGenSrc,
                 sourceModules,
                 testModules,
                 libsSupport);
@@ -136,11 +146,25 @@ private MultiModuleNodeFactory(
      * A builder of the {@link MultiModuleNodeFactory}.
      */
     public static final class Builder {
-        private LibrariesSupport libSupport;
+        private final UpdateHelper helper;
+        private final PropertyEvaluator eval;
+        private final ReferenceHelper refHelper;
+        private LibrariesSupport.Builder libSupport;
         private MultiModule mods;
         private MultiModule testMods;
+        private String procGenSourcesProp;
 
-        private Builder() {
+        private Builder(
+                @NonNull final UpdateHelper helper,
+                @NonNull final PropertyEvaluator eval,
+                @NonNull final ReferenceHelper refHelper) {
+            Parameters.notNull("helper", helper);   //NOI18N
+            Parameters.notNull("eval", eval); //NOI18N
+            Parameters.notNull("refHelper", refHelper); //NOI18N
+            this.helper = helper;
+            this.eval = eval;
+            this.refHelper = refHelper;
+            this.procGenSourcesProp = ProjectProperties.ANNOTATION_PROCESSING_SOURCE_OUTPUT;
         }
 
         /**
@@ -159,8 +183,8 @@ public Builder setSources(
 
         /**
          * Adds project's test modules into the logical view.
-         * @param sourceModules the module roots
-         * @param srcRoots the source roots
+         * @param testModules  the module roots
+         * @param testRoots the source roots
          * @return the {@link Builder}
          */
         @NonNull
@@ -173,17 +197,11 @@ public Builder setTests(
 
         /**
          * Adds libraries nodes into the logical view.
-         * @param helper the {@link UpdateHelper} to resolve paths
-         * @param evaluator the {@link PropertyEvaluator} to access project properties
-         * @param refHelper the {@link ReferenceHelper} to resolve project references
          * @return the {@link Builder}
          */
         @NonNull
-        public Builder addLibrariesNodes(
-                @NonNull final UpdateHelper helper,
-                @NonNull final PropertyEvaluator evaluator,
-                @NonNull final ReferenceHelper refHelper) {
-            libSupport = new LibrariesSupport(helper, evaluator, refHelper);
+        public Builder addLibrariesNodes() {
+            libSupport = new LibrariesSupport.Builder();
             return this;
         }
 
@@ -221,6 +239,19 @@ public Builder addTestLibrariesNodeActions(@NonNull final Action... actions) {
             return this;
         }
 
+        /**
+         * Sets the property holding the location of the annotation processors source output.
+         * Default value is {@link ProjectProperties#ANNOTATION_PROCESSING_SOURCE_OUTPUT}
+         * @param propName the name of the property.
+         * @return the {@link Builder}
+         */
+        @NonNull
+        public Builder setAnnotationProcessorsGeneratedSourcesProperty(@NonNull final String propName) {
+            Parameters.notNull("propName", propName);   //NOI18N
+            this.procGenSourcesProp = propName;
+            return this;
+        }
+
         /**
          * Builds the {@link MultiModuleNodeFactory}.
          * @return the new {@link MultiModuleNodeFactory} instance
@@ -230,21 +261,29 @@ public MultiModuleNodeFactory build() {
             return new MultiModuleNodeFactory(
                     mods,
                     testMods,
-                    libSupport);
+                    new ProcessorGeneratedSources(helper, eval, procGenSourcesProp),
+                    libSupport == null ? null : libSupport.build(helper, eval, refHelper));
         }
 
         /**
          * Creates a new {@link Builder}.
+         * @param helper the {@link UpdateHelper}
+         * @param eval the {@link PropertyEvaluator}
+         * @param refHelper the {@link ReferenceHelper}
          * @return the {@link Builder}
          */
         @NonNull
-        public static Builder create() {
-            return new Builder();
+        public static Builder create(
+                @NonNull final UpdateHelper helper,
+                @NonNull final PropertyEvaluator eval,
+                @NonNull final ReferenceHelper refHelper) {
+            return new Builder(helper, eval, refHelper);
         }
     }
 
     private static final class Nodes implements NodeList<ModuleKey>, PropertyChangeListener {
         private final Project project;
+        private final ProcessorGeneratedSources procGenSrc;
         private final MultiModule sourceModules;
         private final MultiModule testModules;
         private final LibrariesSupport libsSupport;
@@ -252,11 +291,14 @@ public static Builder create() {
 
         Nodes(
                 @NonNull final Project project,
+                @NonNull final ProcessorGeneratedSources procGenSrc,
                 @NullAllowed final MultiModule sourceModules,
                 @NullAllowed final MultiModule testModules,
                 @NullAllowed final LibrariesSupport libsSupport) {
             Parameters.notNull("project", project);     //NOI18N
+            Parameters.notNull("procGenSrc", procGenSrc);
             this.project = project;
+            this.procGenSrc = procGenSrc;
             this.sourceModules = sourceModules;
             this.testModules = testModules;
             this.libsSupport = libsSupport;
@@ -270,7 +312,7 @@ public static Builder create() {
                     this.testModules == null ? Stream.empty() : this.testModules.getModuleNames().stream())
                 .sorted()
                 .distinct()
-                .map((name) -> new ModuleKey(project, name, sourceModules, testModules, libsSupport))
+                .map((name) -> new ModuleKey(project, name, sourceModules, testModules, procGenSrc, libsSupport))
                 .collect(Collectors.toList());
         }
 
@@ -322,6 +364,7 @@ public void propertyChange(PropertyChangeEvent evt) {
         private final MultiModule sourceModules;
         private final MultiModule testModules;
         private final String moduleName;
+        private final ProcessorGeneratedSources procGenSrc;
         private final LibrariesSupport libsSupport;
 
         ModuleKey(
@@ -329,13 +372,16 @@ public void propertyChange(PropertyChangeEvent evt) {
                 @NonNull final String moduleName,
                 @NullAllowed final MultiModule sourceModules,
                 @NullAllowed final MultiModule testModules,
+                @NonNull final ProcessorGeneratedSources procGenSrc,
                 @NullAllowed final LibrariesSupport libsSupport) {
             Parameters.notNull("project", project);             //NOI18N
             Parameters.notNull("moduleName", moduleName);       //NOI18N
+            Parameters.notNull("procGenSrc", procGenSrc);       //NOI18N
             this.project = project;
             this.moduleName = moduleName;
             this.sourceModules = sourceModules;
             this.testModules = testModules;
+            this.procGenSrc = procGenSrc;
             this.libsSupport = libsSupport;
         }
 
@@ -359,6 +405,11 @@ Project getProject() {
             return project;
         }
 
+        @NonNull
+        ProcessorGeneratedSources getProcessorGeneratedSources() {
+            return procGenSrc;
+        }
+
         @CheckForNull
         LibrariesSupport getLibrariesSupport() {
             return libsSupport;
@@ -842,6 +893,7 @@ public Transferable paste() throws IOException {
         private final MultiModuleGroupQuery groupQuery;
         private final MultiModule srcModule;
         private final MultiModule testModule;
+        private final ProcessorGeneratedSources procGenSrc;
         private final LibrariesSupport libsSupport;
         private final RequestProcessor.Task refresh;
         private final AtomicReference<ClassPath> srcPath;
@@ -855,6 +907,7 @@ private ModuleChildren(final ModuleKey key) {
             this.groupQuery = project.getLookup().lookup(MultiModuleGroupQuery.class);
             this.srcModule = key.getSourceModules();
             this.testModule = key.getTestModules();
+            this.procGenSrc = key.getProcessorGeneratedSources();
             this.libsSupport = key.getLibrariesSupport();
             this.srcPath = new AtomicReference<>();
             this.testPath = new AtomicReference<>();
@@ -876,6 +929,7 @@ protected void addNotify() {
             if (testPath.compareAndSet(null, cp)) {
                 cp.addPropertyChangeListener(this);
             }
+            this.procGenSrc.addPropertyChangeListener(this);
             setKeys(createKeys());
         }
 
@@ -890,6 +944,7 @@ protected void removeNotify() {
             if (cp != null && testPath.compareAndSet(cp, null)) {
                 cp.removePropertyChangeListener(this);
             }
+            this.procGenSrc.removePropertyChangeListener(this);
             setKeys(Collections.emptySet());
         }
 
@@ -897,7 +952,7 @@ protected void removeNotify() {
         @NonNull
         protected Node[] createNodes(@NonNull final Key key) {
             if (key.isSource()) {
-                Node n = PackageView.createPackageView(key.getSourceGroup());
+                Node n = new PackageViewFilterNode(key.getSourceGroup(), this.project, key.isGenerated());
                 MultiModuleGroupQuery.Result r = groupQuery.findModuleInfo(key.getSourceGroup());
                 if (r == null) {
                     if (key.isTests()) {
@@ -983,19 +1038,26 @@ protected void removeNotify() {
                 grpsByRoot.put(g.getRootFolder(), g);
             }
             final Comparator<FileObject> foc = (a,b) -> a.getNameExt().compareTo(b.getNameExt());
-            return Stream.concat(Stream.concat(
-                    Arrays.stream(sourceP.getRoots())
-                        .sorted(foc)
-                        .map((fo) -> Pair.of(fo,false)),
-                    Arrays.stream(testPath.get().getRoots())
-                        .sorted(foc)
-                        .map((fo) -> Pair.of(fo,true)))
-                    .map((p) -> {
-                        final SourceGroup g = grpsByRoot.get(p.first());
-                        return g == null ?
-                                null :
-                                new Key(g,p.second());
-                     })
+            return Stream.concat(
+                    Stream.concat(
+                        Stream.concat(
+                            Arrays.stream(sourceP.getRoots())
+                                .sorted(foc)
+                                .map((fo) -> Pair.of(fo,false)),
+                            Arrays.stream(testPath.get().getRoots())
+                                .sorted(foc)
+                                .map((fo) -> Pair.of(fo,true))
+                        )
+                        .map((p) -> {
+                            final SourceGroup g = grpsByRoot.get(p.first());
+                            return g == null ?
+                                    null :
+                                    new Key(g, p.second(), false);
+                         }),
+                        Stream.of(Optional.ofNullable(procGenSrc.getGeneratedGroups(moduleName))
+                                .map((sg) -> new Key(sg, false, true))
+                                .orElse(null))
+                    )
                     .filter((p) -> p != null),
                     Stream.of(
                         new Key(sourceP, false),
@@ -1006,7 +1068,8 @@ protected void removeNotify() {
 
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
-            if (ClassPath.PROP_ROOTS.equals(evt.getPropertyName())) {
+            final String propName = evt.getPropertyName();
+            if (ClassPath.PROP_ROOTS.equals(propName) || ProcessorGeneratedSources.PROP_GEN_GROUPS.equals(propName)) {
                 refresh.schedule(100);
             }
         }
@@ -1014,19 +1077,22 @@ public void propertyChange(PropertyChangeEvent evt) {
         private final static class Key {
             private final boolean sources;
             private final boolean tests;
+            private final boolean generated;
             private final SourceGroup sg;
             private final ClassPath sourcePath;
             private final FileObject[] sourceRoots;
 
             Key(
                     @NonNull final SourceGroup sg,
-                    final boolean tests) {
+                    final boolean tests,
+                    final boolean generated) {
                 assert sg != null;
                 this.sources = true;
                 this.sg = sg;
                 this.sourcePath = null;
                 this.sourceRoots = new FileObject[0];
                 this.tests = tests;
+                this.generated = generated;
             }
 
             Key(
@@ -1038,6 +1104,7 @@ public void propertyChange(PropertyChangeEvent evt) {
                 this.sourcePath = sourcePath;
                 this.sourceRoots = this.sourcePath.getRoots();
                 this.tests = tests;
+                this.generated = false;
             }
 
             boolean isSource() {
@@ -1048,6 +1115,13 @@ boolean isTests() {
                 return tests;
             }
 
+            boolean isGenerated() {
+                if (!sources) {
+                    throw new IllegalStateException("Not a source key.");   //NOI18N
+                }
+                return generated;
+            }
+
             @NonNull
             SourceGroup getSourceGroup() {
                 if (!sources) {
@@ -1075,8 +1149,9 @@ private ClassPath getSourcePath() {
             @Override
             public int hashCode() {
                 int res = 17;
-                res = res * 31 + (sources ? 0 : 1);
-                res = res * 31 + (tests ? 0 : 1);
+                res = res * 31 + (sources ? 1 : 0);
+                res = res * 31 + (tests ? 1 : 0);
+                res = res * 31 + (generated ? 1 : 0);
                 res = res * 31 + Optional.ofNullable(sg).map(SourceGroup::getRootFolder).map(Object::hashCode).orElse(0);
                 res = res * 31 + (sourceRoots.length == 0 ? 0 : 1);
                 return res;
@@ -1093,6 +1168,7 @@ public boolean equals(Object obj) {
                 final Key other = (Key) obj;
                 return  (sources == other.sources) &&
                         (tests == other.tests) &&
+                        (generated == other.generated) &&
                         sgEq(sg, other.sg) &&
                         (sourceRoots.length == 0 ? other.sourceRoots.length == 0 : other.sourceRoots.length != 0);
             }
@@ -1119,22 +1195,26 @@ private static boolean sgEq(
         private final PropertyEvaluator evaluator;
         private final ReferenceHelper refHelper;
         private final ClassPathSupport cs;
-        private final List<Action> actions;
-        private final List<Action> testActions;
+        private final List<? extends Action> actions;
+        private final List<? extends Action> testActions;
 
         LibrariesSupport(
                 @NonNull final UpdateHelper helper,
                 @NonNull final PropertyEvaluator evaluator,
-                @NonNull final ReferenceHelper refHelper) {
+                @NonNull final ReferenceHelper refHelper,
+                @NonNull final List<? extends Action> actions,
+                @NonNull final List<? extends Action> testActions) {
             Parameters.notNull("helper", helper);   //NOI18N
             Parameters.notNull("evaluator", evaluator); //NOI18N
             Parameters.notNull("refHelper", refHelper); //NOI18N
+            Parameters.notNull("actions", actions);     //NOI18N
+            Parameters.notNull("testActions", testActions); //NOI18N
             this.helper = helper;
             this.evaluator = evaluator;
             this.refHelper = refHelper;
             this.cs = new ClassPathSupport(evaluator, refHelper, helper.getAntProjectHelper(), helper, null);
-            this.actions = new ArrayList<>();
-            this.testActions = new ArrayList<>();
+            this.actions = actions;
+            this.testActions = testActions;
         }
 
         @NonNull
@@ -1158,14 +1238,36 @@ ClassPathSupport getClassPathSupport() {
         }
 
         @NonNull
-        Collection<? extends Action>getActions(final boolean tests) {
+        Collection<? extends Action> getActions(final boolean tests) {
             return tests ? testActions : actions;
         }
 
-        void addActions(
-                final boolean tests,
-                @NonNull final Action... actions) {
-            Collections.addAll(tests ? this.testActions : this.actions, actions);
+        private static final class Builder {
+            private final List<Action> actions;
+            private final List<Action> testActions;
+
+            Builder () {
+                this.actions = new ArrayList<>();
+                this.testActions = new ArrayList<>();
+            }
+
+            void addActions(
+                    final boolean tests,
+                    @NonNull final Action... actions) {
+                Collections.addAll(tests ? this.testActions : this.actions, actions);
+            }
+
+            LibrariesSupport build(
+                    @NonNull final UpdateHelper helper,
+                    @NonNull final PropertyEvaluator eval,
+                    @NonNull final ReferenceHelper refHelper) {
+                return new LibrariesSupport(
+                        helper,
+                        eval,
+                        refHelper,
+                        actions,
+                        testActions);
+            }
         }
     }
 
@@ -1223,4 +1325,154 @@ private Node getDataFolderNodeDelegate() {
             return new AbstractNode(Children.LEAF);
         }
     }
+
+    private static final class ProcessorGeneratedSources extends FileChangeAdapter implements PropertyChangeListener {
+        private static final String PROP_GEN_GROUPS = "generatedGroups";    //NOI18N
+        private final UpdateHelper helper;
+        private final PropertyEvaluator eval;
+        private final String sourceOutputProp;
+        private final AtomicBoolean listensOnFs;
+        private final PropertyChangeSupport listeners;
+        //@GuardedBy("this")
+        private Map<String,SourceGroup> cache;
+
+        ProcessorGeneratedSources(
+                @NonNull final UpdateHelper helper,
+                @NonNull final PropertyEvaluator eval,
+                @NonNull final String processorsSourceOutputProp) {
+            Parameters.notNull("helper", helper);   //NOI18N
+            Parameters.notNull("eval", eval);       //NOI18N
+            Parameters.notNull("processorsSourceOutputProp", processorsSourceOutputProp); //NOI18N
+            this.helper = helper;
+            this.eval = eval;
+            this.sourceOutputProp = processorsSourceOutputProp;
+            this.listensOnFs = new AtomicBoolean();
+            this.listeners = new PropertyChangeSupport(this);
+            this.eval.addPropertyChangeListener(WeakListeners.propertyChange(this, this.eval));
+        }
+
+        @CheckForNull
+        SourceGroup getGeneratedGroups(@NonNull final String moduleName) {
+            final Map<String,SourceGroup> cache = getCache();
+            return cache.get(moduleName);
+        }
+
+        void addPropertyChangeListener(@NonNull final PropertyChangeListener l) {
+            this.listeners.addPropertyChangeListener(l);
+        }
+
+        void removePropertyChangeListener(@NonNull final PropertyChangeListener l) {
+            this.listeners.removePropertyChangeListener(l);
+        }
+
+        private Map<String,SourceGroup> getCache() {
+            synchronized (this) {
+                if (cache != null) {
+                    return cache;
+                }
+            }
+            final File genSrc = Optional.ofNullable(eval.getProperty(sourceOutputProp))
+                    .map(helper.getAntProjectHelper()::resolveFile)
+                    .orElse(null);
+            final Map<String,SourceGroup> m = new HashMap<>();
+            if (genSrc != null) {
+                if (listensOnFs.compareAndSet(false, true)) {
+                    FileUtil.addFileChangeListener(this, genSrc);
+                }
+                final FileObject genSrcFo = FileUtil.toFileObject(genSrc);
+                if (genSrcFo != null) {
+                    Arrays.stream(genSrcFo.getChildren())
+                            .filter(FileObject::isFolder)
+                            .forEach((fo) -> m.put(fo.getNameExt(), new APSourceGroup(genSrcFo, fo)));
+                }
+            }
+            synchronized (this) {
+                if (cache == null) {
+                    cache = m;
+                    return m;
+                } else {
+                    return cache;
+                }
+            }
+        }
+
+        @Override
+        public void fileFolderCreated(FileEvent fe) {
+            reset();
+        }
+
+        @Override
+        public void fileDeleted(FileEvent fe) {
+            reset();
+        }
+
+        @Override
+        public void fileRenamed(FileRenameEvent fe) {
+            reset();
+        }
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            final String propName = evt.getPropertyName();
+            if (propName == null || this.sourceOutputProp.equals(propName)) {
+                reset();
+            }
+        }
+
+        private void reset() {
+            synchronized (this) {
+                this.cache = null;
+            }
+            listeners.firePropertyChange(PROP_GEN_GROUPS, null, null);
+        }
+
+        private static final class APSourceGroup implements SourceGroup {
+
+            private final FileObject modSrcPathRoot;
+            private final FileObject srcPathRoot;
+
+            APSourceGroup(
+                    @NonNull final FileObject modSrcPathRoot,
+                    @NonNull final FileObject srcPathRoot) {
+                Parameters.notNull("modSrcPathRoot", modSrcPathRoot);   //NOI18N
+                Parameters.notNull("srcPathRoot", srcPathRoot);   //NOI18N
+                this.modSrcPathRoot = modSrcPathRoot;
+                this.srcPathRoot = srcPathRoot;
+            }
+
+            @Override
+            public FileObject getRootFolder() {
+                return srcPathRoot;
+            }
+
+            @Override
+            public String getName() {
+                return modSrcPathRoot.getNameExt();
+            }
+
+            @Override
+            public String getDisplayName() {
+                return NbBundle.getMessage(
+                        MultiModuleNodeFactory.class,
+                        "MultiModuleNodeFactory.gensrc",
+                        getName());
+            }
+
+            @Override
+            public Icon getIcon(boolean opened) {
+                return null;
+            }
+
+            @Override
+            public boolean contains(FileObject file) {
+                return true;
+            }
+
+            @Override
+            public void addPropertyChangeListener(PropertyChangeListener listener) {}
+
+            @Override
+            public void removePropertyChangeListener(PropertyChangeListener listener) {}
+        }
+    }
 }
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/PackageViewFilterNode.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/PackageViewFilterNode.java
new file mode 100644
index 000000000..48c546661
--- /dev/null
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/PackageViewFilterNode.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.api.common.project.ui;
+
+import java.awt.event.ActionEvent;
+import java.io.CharConversionException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.spi.java.project.support.ui.PackageView;
+import org.netbeans.spi.project.ui.CustomizerProvider;
+import org.netbeans.spi.project.ui.CustomizerProvider2;
+import org.openide.nodes.FilterNode;
+import org.openide.util.NbBundle;
+import org.openide.xml.XMLUtil;
+
+/**
+ * Adjusts some display characteristics of source group root node in logical the view.
+ * @author Tomas Zezula
+ */
+final class PackageViewFilterNode extends FilterNode {
+
+    private final Project project;
+    private final String customizerNodeName;
+    private final String customizerPanelName;
+    private final boolean trueSource;
+
+    PackageViewFilterNode(
+            @NonNull final SourceGroup sourceGroup,
+            @NonNull final Project project,
+            final boolean generated) {
+        this(sourceGroup, project, "Sources", null, generated); //NOI18N
+    }
+
+    PackageViewFilterNode(
+            @NonNull final SourceGroup sourceGroup,
+            @NonNull final Project project,
+            @NonNull final String customizerNodeName,
+            @NullAllowed final String customizerPanelName,
+            final boolean generated) {
+        super(PackageView.createPackageView(sourceGroup));
+        this.project = project;
+        this.customizerNodeName = customizerNodeName;
+        this.customizerPanelName = customizerPanelName;
+        this.trueSource = !generated;
+    }
+
+    @Override
+    public Action[] getActions(boolean context) {
+        List<Action> actions = new ArrayList<>();
+        Collections.addAll(actions, super.getActions(context));
+        if (trueSource) {
+            actions.add(null);
+            actions.add(new PreselectPropertiesAction(project, customizerNodeName, customizerPanelName));
+        } else {
+            // Just take out "New File..." as this would be misleading.
+            Iterator<Action> scan = actions.iterator();
+            while (scan.hasNext()) {
+                Action a = scan.next();
+                if (a != null && a.getClass().getName().equals("org.netbeans.modules.project.ui.actions.NewFile$WithSubMenu")) { // NOI18N
+                    scan.remove();
+                }
+            }
+        }
+        return actions.toArray(new Action[actions.size()]);
+    }
+
+    @Override
+    public String getHtmlDisplayName() {
+        if (trueSource) {
+            return super.getHtmlDisplayName();
+        }
+        String htmlName = getOriginal().getHtmlDisplayName();
+        if (htmlName == null) {
+            try {
+                htmlName = XMLUtil.toElementContent(super.getDisplayName());
+            } catch (CharConversionException x) {
+                return null; // never mind
+            }
+        }
+        return "<font color='!controlShadow'>" + htmlName + "</font>"; // NOI18N
+    }
+}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/PreselectPropertiesAction.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/PreselectPropertiesAction.java
new file mode 100644
index 000000000..8d59467be
--- /dev/null
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/PreselectPropertiesAction.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.api.common.project.ui;
+
+import java.awt.event.ActionEvent;
+import javax.swing.AbstractAction;
+import org.netbeans.api.project.Project;
+import org.netbeans.spi.project.ui.CustomizerProvider;
+import org.netbeans.spi.project.ui.CustomizerProvider2;
+import org.openide.util.NbBundle;
+
+/**
+ * Action to preselect a category in the project's properties.
+ * @author Tomas Zezula
+ */
+final class PreselectPropertiesAction extends AbstractAction {
+    private final Project project;
+    private final String nodeName;
+    private final String panelName;
+
+    PreselectPropertiesAction(Project project, String nodeName, String panelName) {
+        super(NbBundle.getMessage(PreselectPropertiesAction.class, "LBL_Properties_Action"));
+        this.project = project;
+        this.nodeName = nodeName;
+        this.panelName = panelName;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        CustomizerProvider2 cp2 = project.getLookup().lookup(CustomizerProvider2.class);
+        if (cp2 != null) {
+            cp2.showCustomizer(nodeName, panelName);
+        } else {
+            CustomizerProvider cp = project.getLookup().lookup(CustomizerProvider.class);
+            if (cp != null) {
+                cp.showCustomizer();
+            }
+        }
+    }
+}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/ProjectUISupport.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/ProjectUISupport.java
index e68008988..e1df20de9 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/ProjectUISupport.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/ProjectUISupport.java
@@ -108,7 +108,7 @@ public static SourceGroup createLibrariesSourceGroup(FileObject root, String dis
      * Create action which opens project properties on the given panel.
      */
     public static AbstractAction createPreselectPropertiesAction(Project project, String nodeName, String panelName) {
-        return new JavaSourceNodeFactory.PreselectPropertiesAction(project, nodeName, panelName);
+        return new PreselectPropertiesAction(project, nodeName, panelName);
     }
 
     /**
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/queries/AutomaticModuleNameCompilerOptionsQueryImpl.java b/java.api.common/src/org/netbeans/modules/java/api/common/queries/AutomaticModuleNameCompilerOptionsQueryImpl.java
new file mode 100644
index 000000000..b6c6ee7cf
--- /dev/null
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/queries/AutomaticModuleNameCompilerOptionsQueryImpl.java
@@ -0,0 +1,329 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.api.common.queries;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.java.api.common.SourceRoots;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.netbeans.spi.project.support.ant.AntProjectHelper;
+import org.netbeans.spi.project.support.ant.PropertyEvaluator;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileRenameEvent;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.ChangeSupport;
+import org.openide.util.Parameters;
+import org.openide.util.WeakListeners;
+
+/**
+ * The {@link CompilerOptionsQueryImplementation} for automatic module name stored in manifest.
+ * @author Tomas Zezula
+ */
+final class AutomaticModuleNameCompilerOptionsQueryImpl implements CompilerOptionsQueryImplementation {
+
+    private static final Logger LOG = Logger.getLogger(AutomaticModuleNameCompilerOptionsQueryImpl.class.getName());
+    private static final String OPT_AUTOMATIC_MODULE_NAME = "-XDautomatic-module-name";  //NOI18N
+    private static final String ATTR_AUTOMATIC_MOD_NAME = "Automatic-Module-Name";   //NOI18N
+    private static final String MODULE_INFO_JAVA = "module-info.java";                  //NOI18N
+
+    private final AntProjectHelper helper;
+    private final PropertyEvaluator eval;
+    private final SourceRoots sources;
+    private final String manifestProp;
+    private final AtomicReference<R> result;
+
+    AutomaticModuleNameCompilerOptionsQueryImpl(
+            @NonNull final AntProjectHelper helper,
+            @NonNull final PropertyEvaluator eval,
+            @NonNull final SourceRoots sources,
+            @NonNull final String manifestProp) {
+        Parameters.notNull("helper", helper);   //NOI18N
+        Parameters.notNull("eval", eval);       //NOI18N
+        Parameters.notNull("sources", sources); //NOI18N
+        Parameters.notNull("manifestProp", manifestProp);   //NOI18N
+        this.helper = helper;
+        this.eval = eval;
+        this.sources = sources;
+        this.manifestProp = manifestProp;
+        this.result = new AtomicReference<>();
+    }
+
+    @CheckForNull
+    @Override
+    public Result getOptions(FileObject file) {
+        if (isOwned(file, sources)) {
+            R r = result.get();
+            if (r == null) {
+                r = new R(helper, eval, sources, manifestProp);
+                if (!result.compareAndSet(null, r)) {
+                    r = result.get();
+                }
+            }
+            assert r != null;
+            return r;
+        }
+        return null;
+    }
+
+    private static boolean isOwned(
+            @NonNull final FileObject file,
+            @NonNull final SourceRoots roots) {
+        for (FileObject root : roots.getRoots()) {
+            if (root.equals(file) || FileUtil.isParentOf(root, file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static final class R extends Result implements PropertyChangeListener {
+
+        private final AntProjectHelper helper;
+        private final PropertyEvaluator eval;
+        private final SourceRoots sources;
+        private final String manifestProp;
+        private final ChangeSupport listeners;
+        private final FileChangeListener manifestListener;
+        private final FileChangeListener modInfoListener;
+        //@GuardedBy("this")
+        private List<? extends String> cache;
+        //@GuardedBy("this")
+        private File currentManifestFile;
+        //@GuardedBy("this")
+        private Collection<? extends File> currentModuleInfos = Collections.emptySet();
+
+        R(
+                @NonNull final AntProjectHelper helper,
+                @NonNull final PropertyEvaluator eval,
+                @NonNull final SourceRoots sources,
+                @NonNull final String manifestProp) {
+            this.helper = helper;
+            this.eval = eval;
+            this.sources = sources;
+            this.manifestProp = manifestProp;
+            this.listeners = new ChangeSupport(this);
+            Set<FCL.Op> filter = EnumSet.allOf(FCL.Op.class);
+            filter.remove(FCL.Op.FILE_ATTR_CHANGED);
+            this.manifestListener = new FCL(this::reset, filter);
+            filter = EnumSet.allOf(FCL.Op.class);
+            filter.remove(FCL.Op.FILE_ATTR_CHANGED);
+            filter.remove(FCL.Op.FILE_CHANGED);
+            this.modInfoListener = new FCL(this::reset, filter);
+            this.eval.addPropertyChangeListener(WeakListeners.propertyChange(this, this.eval));
+            this.sources.addPropertyChangeListener(WeakListeners.propertyChange(this, this.sources));
+        }
+
+        @Override
+        public List<? extends String> getArguments() {
+            List<? extends String> res;
+            File lastManifestFile;
+            Collection<? extends File> lastModuleInfos;
+            synchronized (this) {
+                res = cache;
+                lastManifestFile = currentManifestFile;
+                lastModuleInfos = currentModuleInfos;
+            }
+            File manifestFile = null;
+            final Collection<File> moduleInfos = new ArrayList<>();
+            if (res == null) {
+                res = Collections.emptyList();
+                if (!hasModuleInfo(moduleInfos)) {
+                    manifestFile = Optional.ofNullable(eval.getProperty(manifestProp))
+                            .map(helper::resolveFile)
+                            .orElse(null);
+                    if (manifestFile != null) {
+                        if (manifestFile.isFile() && manifestFile.canRead()) {
+                            try {
+                                try(InputStream in = new BufferedInputStream(new FileInputStream(manifestFile))) {
+                                    final Manifest manifest = new Manifest(in);
+                                    final String moduleName = manifest.getMainAttributes().getValue(ATTR_AUTOMATIC_MOD_NAME);
+                                    if (moduleName != null) {
+                                        res = Collections.singletonList(String.format(
+                                                "%s:%s",    //NOI18N
+                                                OPT_AUTOMATIC_MODULE_NAME,
+                                                moduleName));
+                                    }
+                                }
+                            } catch (IOException ioe) {
+                                LOG.log(
+                                        Level.WARNING,
+                                        "Cannot read: {0}, reason: {1}",    //NOI18N
+                                        new Object[]{
+                                            manifestFile.getAbsolutePath(),
+                                            ioe.getMessage()
+                                        });
+                            }
+                        }
+                    }
+                }
+                synchronized (this) {
+                    if (cache == null) {
+                        cache = res;
+                        final boolean sameManifests = Objects.equals(lastManifestFile,manifestFile);
+                        if (lastManifestFile != null && !sameManifests) {
+                            FileUtil.removeFileChangeListener(this.manifestListener, lastManifestFile);
+                        }
+                        if (manifestFile != null && !sameManifests) {
+                            FileUtil.addFileChangeListener(this.manifestListener, manifestFile);
+                        }
+                        currentManifestFile = manifestFile;
+                        for (File f : lastModuleInfos) {
+                            FileUtil.removeFileChangeListener(this.modInfoListener, f);
+                        }
+                        for (File f : moduleInfos) {
+                            FileUtil.addFileChangeListener(this.modInfoListener, f);
+                        }
+                        currentModuleInfos = moduleInfos;
+                    } else {
+                        res = cache;
+                    }
+                }
+            }
+            return res;
+        }
+
+        @Override
+        public void addChangeListener(@NonNull final ChangeListener listener) {
+            this.listeners.addChangeListener(listener);
+        }
+
+        @Override
+        public void removeChangeListener(@NonNull final ChangeListener listener) {
+            this.listeners.removeChangeListener(listener);
+        }
+
+        @Override
+        public void propertyChange(@NonNull final PropertyChangeEvent evt) {
+            final String propName = evt.getPropertyName();
+            final Object from = evt.getSource();
+            if (from == sources && SourceRoots.PROP_ROOTS.equals(propName)) {
+                reset();
+            } else if (from == eval && (propName == null || propName.equals(this.manifestProp))) {
+                reset();
+            }
+        }
+
+        private boolean hasModuleInfo(final Collection<? super File> moduleInfos) {
+            boolean vote = false;
+            for (FileObject root : sources.getRoots()) {
+                if (root.getFileObject(MODULE_INFO_JAVA) != null) {
+                    vote = true;
+                }
+                Optional.ofNullable(FileUtil.toFile(root))
+                        .map((f) -> new File(f, MODULE_INFO_JAVA))
+                        .ifPresent(moduleInfos::add);
+            }
+            return vote;
+        }
+
+        private void reset() {
+            synchronized (this) {
+                this.cache = null;
+            }
+            this.listeners.fireChange();
+        }
+    }
+
+    private static final class FCL implements FileChangeListener {
+
+        private static enum Op {
+            FILE_CREATED,
+            FOLDER_CREATED,
+            FILE_CHANGED,
+            FILE_DELETED,
+            FILE_RENAMED,
+            FILE_ATTR_CHANGED
+        }
+
+        private final Runnable action;
+        private final Set<? extends Op> filter;
+
+        FCL(
+                @NonNull final Runnable action,
+                @NonNull final Set<? extends Op> filter) {
+            this.action = action;
+            this.filter = filter;
+        }
+
+        @Override
+        public void fileFolderCreated(FileEvent fe) {
+            if (filter.contains(Op.FOLDER_CREATED)) {
+                action.run();
+            }
+        }
+
+        @Override
+        public void fileDataCreated(FileEvent fe) {
+            if (filter.contains(Op.FILE_CREATED)) {
+                action.run();
+            }
+        }
+
+        @Override
+        public void fileChanged(FileEvent fe) {
+            if (filter.contains(Op.FILE_CHANGED)) {
+                action.run();
+            }
+        }
+
+        @Override
+        public void fileDeleted(FileEvent fe) {
+            if (filter.contains(Op.FILE_DELETED)) {
+                action.run();
+            }
+        }
+
+        @Override
+        public void fileRenamed(FileRenameEvent fe) {
+            if (filter.contains(Op.FILE_RENAMED)) {
+                action.run();
+            }
+        }
+
+        @Override
+        public void fileAttributeChanged(FileAttributeEvent fe) {
+            if (filter.contains(Op.FILE_ATTR_CHANGED)) {
+                action.run();
+            }
+        }
+    }
+}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/queries/QuerySupport.java b/java.api.common/src/org/netbeans/modules/java/api/common/queries/QuerySupport.java
index 6e7144ee0..e2657ca69 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/queries/QuerySupport.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/queries/QuerySupport.java
@@ -330,6 +330,24 @@ public static CompilerOptionsQueryImplementation createUnitTestsCompilerOptionsQ
         return new UnitTestsCompilerOptionsQueryImpl(eval, srcRoots, testRoots);
     }
 
+    /**
+     * Creates an Automatic-Module-Name query.
+     * @param helper the {@link AntProjectHelper}
+     * @param eval the {@link PropertyEvaluator}
+     * @param srcRoots the source roots
+     * @param manifestProperty  the property holding the path to the manifest file
+     * @return a {@link CompilerOptionsQueryImplementation} to find out the Automatic-Module-Name
+     * @since 1.122
+     */
+    @NonNull
+    public static CompilerOptionsQueryImplementation createAutomaticModuleNameQuery(
+            @NonNull final AntProjectHelper helper,
+            @NonNull final PropertyEvaluator eval,
+            @NonNull final SourceRoots srcRoots,
+            @NonNull final String manifestProperty) {
+        return new AutomaticModuleNameCompilerOptionsQueryImpl(helper, eval, srcRoots, manifestProperty);
+    }
+
     /**
      * Create a new query to set up explicit compiler options needed for unit test compilation in a multi module project.
      * @param project the project to create a query for
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImpl.java b/java.api.common/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImpl.java
index 905197533..6db04153c 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImpl.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImpl.java
@@ -359,7 +359,7 @@ private static String getModuleName(@NonNull final FileObject moduleInfo) {
                         return Collections.emptyList();
                     }
                     final List<String> result = Arrays.asList(
-                        String.format("-Xmodule:%s", moduleName),      //NOI18N
+                        String.format("-XD-Xmodule:%s", moduleName),      //NOI18N
                         "--add-reads",  //NOI18N
                         String.format("%s=ALL-UNNAMED", moduleName));  //NOI18N                                    
                     return Collections.unmodifiableList(result);
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/util/CommonModuleUtils.java b/java.api.common/src/org/netbeans/modules/java/api/common/util/CommonModuleUtils.java
index 8798602e2..85c52a4d9 100644
--- a/java.api.common/src/org/netbeans/modules/java/api/common/util/CommonModuleUtils.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/util/CommonModuleUtils.java
@@ -53,7 +53,7 @@
 
     private static final String ARG_ADDMODS = "--add-modules";       //NOI18N
     private static final String ARG_PATCH_MOD = "--patch-module";   //NOI18N
-    private static final String ARG_XMODULE = "-Xmodule";      //NOI18N
+    private static final String ARG_XMODULE = "-XD-Xmodule";      //NOI18N
     private static final Pattern MATCHER_XMODULE =
             Pattern.compile(String.format("%s:(\\S+)", ARG_XMODULE));  //NOI18N
     private static final Pattern MATCHER_PATCH =
diff --git a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/ModuleClassPathsTest.java b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/ModuleClassPathsTest.java
index 2b11a0c73..200ad21d4 100644
--- a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/ModuleClassPathsTest.java
+++ b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/ModuleClassPathsTest.java
@@ -647,8 +647,12 @@ private FileObject createPatchFolder(final String name) throws IOException {
         Collections.sort(res, LEX_COMPARATOR);
         return res;
     }
-    
-    private Collection<URL> reads(
+
+    static Collection<URL> unnamedReads(@NonNull final ClassPath base) throws IOException {
+        return reads(base, NamePredicate.create("java.se").or(NON_JAVA_PUB));  //NOI18N
+    }
+
+    private static Collection<URL> reads(
             @NonNull final ClassPath base,
             @NonNull final Predicate<ModuleElement> predicate,
             @NonNull final FileObject... additionalRoots) throws IOException {
diff --git a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/MultiModuleClassPathProviderTest.java b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/MultiModuleClassPathProviderTest.java
index b3b9e83e6..a9d96eda7 100644
--- a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/MultiModuleClassPathProviderTest.java
+++ b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/classpath/MultiModuleClassPathProviderTest.java
@@ -36,6 +36,7 @@
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.zip.ZipOutputStream;
+import javax.swing.event.ChangeListener;
 import static junit.framework.TestCase.assertTrue;
 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.api.java.classpath.ClassPath;
@@ -56,6 +57,7 @@
 import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider;
 import org.netbeans.spi.java.classpath.ClassPathProvider;
 import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation;
+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
 import org.netbeans.spi.project.support.ant.AntProjectHelper;
 import org.netbeans.spi.project.support.ant.EditableProperties;
 import org.openide.filesystems.FileObject;
@@ -128,6 +130,30 @@ protected void setUp() throws Exception {
                     testModules,
                     testSources);
         });
+        factories.put(SourceLevelQueryImplementation2.class, (prj) -> {
+            return new SourceLevelQueryImplementation2 () {
+                private final SourceLevelQueryImplementation2.Result RES = new SourceLevelQueryImplementation2.Result() {
+                    @Override
+                    public String getSourceLevel() {
+                        return "9"; //NOI18N
+                    }
+                    @Override
+                    public void addChangeListener(ChangeListener listener) {
+                    }
+                    @Override
+                    public void removeChangeListener(ChangeListener listener) {
+                    }
+                };
+                @Override
+                public SourceLevelQueryImplementation2.Result getSourceLevel(FileObject javaFile) {
+                    if (javaFile == prj.getProjectDirectory() || FileUtil.isParentOf(prj.getProjectDirectory(), javaFile)) {
+                        return RES;
+                    } else {
+                        return null;
+                    }
+                }
+            };
+        });
         MockLookup.setInstances(TestProject.createProjectType(factories), new MockJPProvider());
         final FileObject wd = FileUtil.toFileObject(FileUtil.normalizeFile(getWorkDir()));
         src1 = wd.createFolder("src1"); //NOI18N
@@ -316,7 +342,7 @@ public void testBootPath() {
         assertSame(bcp, bcp2);
     }
 
-    public void testGetProjectClassPaths() {
+    public void testGetProjectClassPaths() throws IOException {
         if (systemModules == null) {
             System.out.println("No Java 9, skipping testBootPath"); //NOI18N
             return;
@@ -383,9 +409,9 @@ public void testGetProjectClassPaths() {
                 add(filter(urls(systemModules),"java.base","java.compiler"));   //NOI18N
                 add(filter(urls(systemModules),"java.base","java.xml"));    //NOI18N
                 //Tests - todo not currect now as there is no CompilerOpsQuery
-                add(Collections.emptyList());    //NOI18N
-                add(Collections.emptyList());    //NOI18N
-                add(Collections.emptyList());    //NOI18N
+                add(new ArrayList<>(ModuleClassPathsTest.unnamedReads(systemModules)));
+                add(new ArrayList<>(ModuleClassPathsTest.unnamedReads(systemModules)));
+                add(new ArrayList<>(ModuleClassPathsTest.unnamedReads(systemModules)));
                 add(filter(urls(systemModules),"java.base"));   //NOI18N
             }
         };
diff --git a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/AutomaticModuleNameCompilerOptionsQueryImplTest.java b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/AutomaticModuleNameCompilerOptionsQueryImplTest.java
new file mode 100644
index 000000000..7030137d9
--- /dev/null
+++ b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/AutomaticModuleNameCompilerOptionsQueryImplTest.java
@@ -0,0 +1,295 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.api.common.queries;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.Manifest;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.java.api.common.SourceRoots;
+import org.netbeans.modules.java.api.common.TestProject;
+import org.netbeans.modules.java.api.common.project.ProjectProperties;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.netbeans.spi.project.support.ant.AntProjectHelper;
+import org.netbeans.spi.project.support.ant.EditableProperties;
+import org.openide.filesystems.FileLock;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Mutex;
+import org.openide.util.MutexException;
+import org.openide.util.test.MockLookup;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public class AutomaticModuleNameCompilerOptionsQueryImplTest extends NbTestCase {
+    private TestProject project;
+    private SourceRoots srcRoots;
+
+    public AutomaticModuleNameCompilerOptionsQueryImplTest(final String name) {
+        super(name);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        clearWorkDir();
+        MockLookup.setInstances(TestProject.createProjectType());
+        final FileObject wd = FileUtil.toFileObject(FileUtil.normalizeFile(getWorkDir()));
+        final FileObject src = FileUtil.createFolder(wd,"src"); //NOI18N
+        final FileObject tst = FileUtil.createFolder(wd,"test");    //NOI18N
+        Project p = TestProject.createProject(wd, src, tst);
+        project = p.getLookup().lookup(TestProject.class);
+        assertNotNull(project);
+        srcRoots = project.getSourceRoots();
+        assertNotNull(srcRoots);
+        assertEquals(srcRoots.getRoots().length, 1);
+        assertEquals(srcRoots.getRoots()[0], src);
+    }
+
+    public void testOwner() throws IOException {
+        final AutomaticModuleNameCompilerOptionsQueryImpl q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        assertNotNull(q.getOptions(srcRoots.getRoots()[0]));
+        assertNull(q.getOptions(project.getProjectDirectory()));
+        assertSame(
+                q.getOptions(srcRoots.getRoots()[0]),
+                q.getOptions(srcRoots.getRoots()[0]));
+    }
+
+    public void testAutomaticModuleName() throws IOException {
+        assertNull(getManifest());
+        AutomaticModuleNameCompilerOptionsQueryImpl q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        CompilerOptionsQueryImplementation.Result r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertTrue(r.getArguments().isEmpty());
+        createManifest();
+        assertNotNull(getManifest());
+        q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertTrue(r.getArguments().isEmpty());
+        updateManifest(Collections.singletonMap("Automatic-Module-Name", "org.me.foo"));    //NOI18N
+        q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+        final FileObject modInfo = srcRoots.getRoots()[0].createData("module-info", "java");               //NOI18N
+        q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertTrue(r.getArguments().isEmpty());
+        modInfo.delete();
+        q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+        updateManifest(Collections.emptyMap());
+        q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertTrue(r.getArguments().isEmpty());
+    }
+
+    public void testEvaluatorChanges() throws IOException {
+        updateManifest(Collections.singletonMap("Automatic-Module-Name", "org.me.foo"));    //NOI18N
+        assertNotNull(getManifest());
+        final CompilerOptionsQueryImplementation q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        final CompilerOptionsQueryImplementation.Result r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+        ProjectManager.mutex(true, project).writeAccess(() -> {
+            final EditableProperties ep = project.getUpdateHelper().getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
+            ep.setProperty(ProjectProperties.MANIFEST_FILE, "foo.mf");  //NOI18N
+            project.getUpdateHelper().putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep);
+        });
+        assertTrue(r.getArguments().isEmpty());
+        ProjectManager.mutex(true, project).writeAccess(() -> {
+            final EditableProperties ep = project.getUpdateHelper().getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
+            ep.setProperty(ProjectProperties.MANIFEST_FILE, "manifest.mf");  //NOI18N
+            project.getUpdateHelper().putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep);
+        });
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+    }
+
+    public void testModuleInfoChanges() throws IOException {
+        updateManifest(Collections.singletonMap("Automatic-Module-Name", "org.me.foo"));    //NOI18N
+        assertNotNull(getManifest());
+        final CompilerOptionsQueryImplementation q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        final CompilerOptionsQueryImplementation.Result r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+        FileObject modInfo = srcRoots.getRoots()[0].createData("module-info", "java");       //NOI18N
+        assertTrue(r.getArguments().isEmpty());
+        try (FileLock lck = modInfo.lock()) {
+            modInfo.rename(lck, "module-info", "bak"); //NOI18N
+        }
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+        try (FileLock lck = modInfo.lock()) {
+            modInfo.rename(lck, "module-info", "java"); //NOI18N
+        }
+        assertTrue(r.getArguments().isEmpty());
+        modInfo.delete();
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+    }
+
+    public void testManifestChanges() throws IOException {
+        final CompilerOptionsQueryImplementation q = new AutomaticModuleNameCompilerOptionsQueryImpl(
+                project.getUpdateHelper().getAntProjectHelper(),
+                project.getEvaluator(),
+                srcRoots,
+                ProjectProperties.MANIFEST_FILE);
+        final CompilerOptionsQueryImplementation.Result r = q.getOptions(srcRoots.getRoots()[0]);
+        assertNotNull(r);
+        assertTrue(r.getArguments().isEmpty());
+        updateManifest(Collections.singletonMap("Automatic-Module-Name", "org.me.foo"));    //NOI18N
+        assertNotNull(getManifest());
+        assertNotNull(r);
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.foo"),    //NOI18N
+                r.getArguments());
+        updateManifest(Collections.singletonMap("Automatic-Module-Name", "org.me.boo"));    //NOI18N
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.boo"),    //NOI18N
+                r.getArguments());
+        final FileObject mf = getManifest();
+        try (FileLock lck = mf.lock()) {
+            mf.rename(lck, "manifest", "bak"); //NOI18N
+        }
+        assertTrue(r.getArguments().isEmpty());
+        try (FileLock lck = mf.lock()) {
+            mf.rename(lck, "manifest", "mf"); //NOI18N
+        }
+        assertEquals(
+                Collections.singletonList("-XDautomatic-module-name:org.me.boo"),    //NOI18N
+                r.getArguments());
+        mf.delete();
+        assertTrue(r.getArguments().isEmpty());
+    }
+
+    @CheckForNull
+    private FileObject getManifest() throws IOException {
+        return ProjectManager.mutex().readAccess(()-> {
+            return Optional.ofNullable(project.getEvaluator().getProperty(ProjectProperties.MANIFEST_FILE))
+                    .map(project.getUpdateHelper().getAntProjectHelper()::resolveFile)
+                    .map(FileUtil::toFileObject)
+                    .orElse(null);
+        });
+    }
+
+    @NonNull
+    private FileObject createManifest() throws IOException {
+        try {
+            return ProjectManager.mutex().writeAccess((Mutex.ExceptionAction<FileObject>)() -> {
+                String path = project.getEvaluator().getProperty(ProjectProperties.MANIFEST_FILE);
+                if (path == null) {
+                    path = "manifest.mf";   //NOI18N
+                    final EditableProperties props = project.getUpdateHelper().getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
+                    props.setProperty(ProjectProperties.MANIFEST_FILE, path);
+                    project.getUpdateHelper().putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
+                    ProjectManager.getDefault().saveProject(project);
+                }
+                File f =  project.getUpdateHelper().getAntProjectHelper().resolveFile(path);
+                return FileUtil.createData(f);
+            });
+        } catch (MutexException e) {
+            throw e.getCause() instanceof IOException ?
+                    (IOException) e.getCause() :
+                    new IOException(e.getCause());
+        }
+    }
+
+    private void updateManifest(final Map<String,String> attrs) throws IOException {
+        final FileObject mf = createManifest();
+        final Manifest manifest;
+        try(BufferedInputStream in = new BufferedInputStream(mf.getInputStream())) {
+            manifest = new Manifest(in);
+            final Attributes mfAttrs = manifest.getMainAttributes();
+            mfAttrs.clear();
+            mfAttrs.putValue(Name.MANIFEST_VERSION.toString(), "1.0");  //NOI18N
+            for (Map.Entry<String,String> attr : attrs.entrySet()) {
+                mfAttrs.putValue(attr.getKey(), attr.getValue());
+            }
+        }
+        try(BufferedOutputStream out = new BufferedOutputStream(mf.getOutputStream())) {
+            manifest.write(out);
+        }
+    }
+}
diff --git a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImplTest.java b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImplTest.java
index 3898cc545..13e3e730a 100644
--- a/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImplTest.java
+++ b/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/queries/UnitTestsCompilerOptionsQueryImplTest.java
@@ -123,7 +123,7 @@ public void testJDK9_TestInlinedIntoSourceModule() throws IOException {
         final List<? extends String> args = r.getArguments();
         assertEquals(
             Arrays.asList(
-                String.format("-Xmodule:%s", srcModuleName),    //NOI18N
+                String.format("-XD-Xmodule:%s", srcModuleName),    //NOI18N
                 "--add-reads",                                  //NOI18N
                 String.format("%s=ALL-UNNAMED", srcModuleName)  //NOI18N
             ),
@@ -183,7 +183,7 @@ public void testSourceLevelChanges() throws IOException {
         args = r.getArguments();
         assertEquals(
             Arrays.asList(
-                String.format("-Xmodule:%s", srcModuleName),    //NOI18N
+                String.format("-XD-Xmodule:%s", srcModuleName),    //NOI18N
                 "--add-reads",                                  //NOI18N
                 String.format("%s=ALL-UNNAMED", srcModuleName)  //NOI18N
             ),
@@ -214,7 +214,7 @@ public void testRootsChanges() throws IOException {
         args = r.getArguments();
         assertEquals(
             Arrays.asList(
-                String.format("-Xmodule:%s", srcModuleName),    //NOI18N
+                String.format("-XD-Xmodule:%s", srcModuleName),    //NOI18N
                 "--add-reads",  //NOI18N
                 String.format("%s=ALL-UNNAMED", srcModuleName) //NOI18N
             ),
@@ -238,7 +238,7 @@ public void testModuleInfoCreation() throws IOException {
         args = r.getArguments();
         assertEquals(
             Arrays.asList(
-                String.format("-Xmodule:%s", srcModuleName),    //NOI18N
+                String.format("-XD-Xmodule:%s", srcModuleName),    //NOI18N
                 "--add-reads",                                  //NOI18N
                 String.format("%s=ALL-UNNAMED", srcModuleName)  //NOI18N
             ),
@@ -257,7 +257,7 @@ public void testModuleInfoChanges() throws IOException {
         List<? extends String> args = r.getArguments();
         assertEquals(
             Arrays.asList(
-                String.format("-Xmodule:%s", srcModuleName),    //NOI18N
+                String.format("-XD-Xmodule:%s", srcModuleName),    //NOI18N
                 "--add-reads",                                  //NOI18N
                 String.format("%s=ALL-UNNAMED", srcModuleName)  //NOI18N
             ),
@@ -270,7 +270,7 @@ public void testModuleInfoChanges() throws IOException {
         args = r.getArguments();
         assertEquals(
             Arrays.asList(
-                String.format("-Xmodule:%s", newSrcModuleName),     //NOI18N
+                String.format("-XD-Xmodule:%s", newSrcModuleName),     //NOI18N
                 "--add-reads",                                      //NOI18N
                 String.format("%s=ALL-UNNAMED", newSrcModuleName)   //NOI18N
             ),
diff --git a/java.j2semodule/manifest.mf b/java.j2semodule/manifest.mf
index 4608d3395..e01e57da1 100644
--- a/java.j2semodule/manifest.mf
+++ b/java.j2semodule/manifest.mf
@@ -3,5 +3,5 @@ AutoUpdate-Show-In-Client: true
 OpenIDE-Module: org.netbeans.modules.java.j2semodule
 OpenIDE-Module-Layer: org/netbeans/modules/java/j2semodule/ui/resources/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/j2semodule/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.2
+OpenIDE-Module-Specification-Version: 1.3
 
diff --git a/java.j2semodule/nbproject/project.xml b/java.j2semodule/nbproject/project.xml
index 48381df7b..07912272a 100644
--- a/java.j2semodule/nbproject/project.xml
+++ b/java.j2semodule/nbproject/project.xml
@@ -101,7 +101,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>0-1</release-version>
-                        <specification-version>1.120</specification-version>
+                        <specification-version>1.121</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git a/java.j2semodule/src/org/netbeans/modules/java/j2semodule/resources/build-impl.xsl b/java.j2semodule/src/org/netbeans/modules/java/j2semodule/resources/build-impl.xsl
index 4025cdb6f..48d4a0111 100644
--- a/java.j2semodule/src/org/netbeans/modules/java/j2semodule/resources/build-impl.xsl
+++ b/java.j2semodule/src/org/netbeans/modules/java/j2semodule/resources/build-impl.xsl
@@ -489,7 +489,11 @@ is divided into following sections:
                         <mkdir dir="${{empty.dir}}"/>
                         <mkdir dir="@{{apgeneratedsrcdir}}"/>
                         <condition property="processormodulepath.set">
-                            <length string="@{{toString:processormodulepath}}" when="greater" length="0"/>
+                            <resourcecount when="greater" count="0">
+                                <path>
+                                    <pathelement path="@{{processormodulepath}}"/>
+                                </path>
+                            </resourcecount>
                         </condition>
                         <javac>
                             <xsl:attribute name="destdir">@{destdir}</xsl:attribute>
diff --git a/java.j2semodule/src/org/netbeans/modules/java/j2semodule/ui/ModuleNodeFactory.java b/java.j2semodule/src/org/netbeans/modules/java/j2semodule/ui/ModuleNodeFactory.java
index d77bb7ad6..18055ef13 100644
--- a/java.j2semodule/src/org/netbeans/modules/java/j2semodule/ui/ModuleNodeFactory.java
+++ b/java.j2semodule/src/org/netbeans/modules/java/j2semodule/ui/ModuleNodeFactory.java
@@ -54,10 +54,10 @@ public ModuleNodeFactory() {
 
     @NonNull
     private NodeFactory getDelegate(@NonNull final J2SEModularProject mp) {
-        final NodeFactory res = MultiModuleNodeFactory.Builder.create()
+        final NodeFactory res = MultiModuleNodeFactory.Builder.create(mp.getUpdateHelper(), mp.evaluator(), mp.getReferenceHelper())
                 .setSources(mp.getModuleRoots(),mp.getSourceRoots())
                 .setTests(mp.getTestModuleRoots(), mp.getTestSourceRoots())
-                .addLibrariesNodes(mp.getUpdateHelper(), mp.evaluator(), mp.getReferenceHelper())
+                .addLibrariesNodes()
                 .addLibrariesNodeActions(
                     LibrariesNode.createAddProjectAction(mp, mp.getSourceRoots()),
                     LibrariesNode.createAddLibraryAction(mp.getReferenceHelper(), mp.getSourceRoots(), null),
diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultClassPathProvider.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultClassPathProvider.java
index 15ad73464..453062b9e 100644
--- a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultClassPathProvider.java
+++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultClassPathProvider.java
@@ -158,6 +158,8 @@ public ClassPath findClassPath(FileObject file, String type) {
                 }
             } else if (JavaClassPathConstants.MODULE_COMPILE_PATH.equals(type) && hasJava9(file, true) != null) {
                 return getModulePath();
+            } else if (JavaClassPathConstants.MODULE_CLASS_PATH.equals(type) && hasJava9(file, true) != null) {
+                return getCompiledClassPath();
             }
         } else if (CLASS_EXT.equals(file.getExt())) {
             if (ClassPath.BOOT.equals (type)) {
diff --git a/java.j2seproject/nbproject/project.xml b/java.j2seproject/nbproject/project.xml
index 80e868ea9..bda3c75f9 100644
--- a/java.j2seproject/nbproject/project.xml
+++ b/java.j2seproject/nbproject/project.xml
@@ -31,7 +31,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>3</release-version>
-                        <specification-version>3.73</specification-version>
+                        <specification-version>3.85</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -118,7 +118,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>0-1</release-version>
-                        <specification-version>1.120</specification-version>
+                        <specification-version>1.123</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties
index d20003cab..7e3198903 100644
--- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties
+++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties
@@ -69,3 +69,10 @@ J2SEConfigurationProvider.default.label=<default config>
 J2SEProject.too_new=This version of the IDE is too old to read metadata in {0}.
 
 
+#J2SEActionProvider
+LBL_CompileOnSaveUpdate=Compile on Save Update
+TXT_CosUpdateActive=<html>Compile on Save Update running.<br>Please wait...
+TXT_StopBuild=Cancel
+MNE_StopBuild=C
+AN_StopBuild=Cancel
+AD_StopBuild=Cancel build
\ No newline at end of file
diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java
index 18ce68437..547714276 100644
--- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java
+++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java
@@ -27,25 +27,42 @@
 import java.io.OutputStream;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.stream.Stream;
+import javax.swing.JButton;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
 import javax.swing.event.ChangeListener;
-import org.apache.tools.ant.module.api.support.ActionUtils;
+import org.apache.tools.ant.module.api.AntProjectCookie;
+import org.apache.tools.ant.module.api.AntTargetExecutor;
+import org.apache.tools.ant.module.api.support.AntScriptUtils;
 import org.netbeans.api.annotations.common.CheckForNull;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.java.project.JavaProjectConstants;
 import org.netbeans.api.java.source.BuildArtifactMapper;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.project.FileOwnerQuery;
 import org.netbeans.api.project.Project;
 import org.netbeans.modules.java.api.common.SourceRoots;
@@ -61,19 +78,33 @@
 import org.netbeans.spi.project.ProjectServiceProvider;
 import org.netbeans.spi.project.SingleMethod;
 import org.netbeans.spi.project.support.ant.PropertyEvaluator;
+import org.netbeans.spi.project.support.ant.PropertyUtils;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.LifecycleManager;
+import org.openide.NotifyDescriptor;
 import org.openide.execution.ExecutorTask;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
 import org.openide.filesystems.FileLock;
 import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileRenameEvent;
 import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectNotFoundException;
 import org.openide.modules.Places;
 import org.openide.util.BaseUtilities;
 import org.openide.util.ChangeSupport;
 import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.Pair;
 import org.openide.util.Parameters;
 import org.openide.util.RequestProcessor;
 import org.openide.util.WeakListeners;
 import org.openide.util.lookup.ServiceProvider;
+import org.openide.xml.XMLUtil;
 
 /** Action provider of the J2SE project. This is the place where to do
  * strange things to J2SE actions. E.g. compile-single.
@@ -254,6 +285,67 @@ protected boolean isCompileOnSaveUpdate() {
         return names;
     }
 
+    @Override
+    public void invokeAction(String command, Lookup context) throws IllegalArgumentException {
+        final Runnable superCall = () -> super.invokeAction(command, context);
+        if (isCompileOnSaveUpdate() && cosAction.getTarget() != null && getScanSensitiveActions().contains(command)) {
+            LifecycleManager.getDefault ().saveAll ();  //Need to do saveAll eagerly
+            final JButton stopButton = new JButton(NbBundle.getMessage(J2SEActionProvider.class, "TXT_StopBuild"));
+            stopButton.setMnemonic(NbBundle.getMessage(J2SEActionProvider.class, "MNE_StopBuild").charAt(0));
+            stopButton.getAccessibleContext().setAccessibleName(NbBundle.getMessage(J2SEActionProvider.class, "AN_StopBuild"));
+            stopButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(J2SEActionProvider.class, "AD_StopBuild"));
+
+            final AtomicBoolean showState = new AtomicBoolean(true);
+            final AtomicBoolean stopState = new AtomicBoolean();
+
+            try {
+                final JavaSource js = createSource();
+                js.runWhenScanFinished((cc) -> {
+                    cosAction.newSyncTask(() -> SwingUtilities.invokeLater(() -> {
+                        showState.set(false);
+                        final boolean cancelled = stopState.get();
+                        stopButton.doClick();
+                        if (!cancelled) {
+                            superCall.run();
+                        }
+                    }));
+                }, false);
+                final Timer timer = new Timer(1_000, (e) -> {
+                    if (showState.get()) {
+                        final NotifyDescriptor nd = new NotifyDescriptor(
+                            NbBundle.getMessage(J2SEActionProvider.class, "TXT_CosUpdateActive"),
+                            command,
+                            NotifyDescriptor.YES_NO_OPTION,
+                            NotifyDescriptor.INFORMATION_MESSAGE,
+                            new Object[] {stopButton},
+                            null);
+                        final Object res = DialogDisplayer.getDefault().notify(nd);
+                        if (res == DialogDescriptor.CLOSED_OPTION || res == stopButton) {
+                            stopState.set(true);
+                        }
+                    }
+                });
+                timer.setRepeats(false);
+                timer.start();
+            } catch (IOException ioe) {
+                Exceptions.printStackTrace(ioe);
+                superCall.run();    //Last resort - try to run it
+            }
+        } else {
+            superCall.run();
+        }
+    }
+
+    @NonNull
+    private static JavaSource createSource() {
+        final ClasspathInfo cpInfo = ClasspathInfo.create(
+                ClassPath.EMPTY,
+                ClassPath.EMPTY,
+                ClassPath.EMPTY);
+        final JavaSource js = JavaSource.create(cpInfo);
+        return js;
+    }
+
     @ProjectServiceProvider(
             service=ActionProvider.class,
             projectTypes={@LookupProvider.Registration.ProjectType(id="org-netbeans-modules-java-j2seproject",position=100)})
@@ -342,7 +434,7 @@ public ClassPath findClassPath(@NonNull FileObject file, @NonNull String type) {
     }
 
     private static final class CosAction implements BuildArtifactMapper.ArtifactsUpdated,
-            CompileOnSaveAction, PropertyChangeListener {
+            CompileOnSaveAction, PropertyChangeListener, FileChangeListener {
         private static Map<Project,Reference<CosAction>> instances = new WeakHashMap<>();
         private static final String COS_UPDATED = "$cos.update";    //NOI18N
         private static final String COS_CUSTOM = "$cos.update.resources";    //NOI18N
@@ -363,6 +455,7 @@ public ClassPath findClassPath(@NonNull FileObject file, @NonNull String type) {
         private final BuildArtifactMapper mapper;
         private final Map</*@GuardedBy("this")*/URL,BuildArtifactMapper.ArtifactsUpdated> currentListeners;
         private final ChangeSupport cs;
+        private final AtomicReference<Pair<URI,Collection<File>>> importantFilesCache;
         private volatile Object targetCache;
         private volatile Object updatedFSProp;
 
@@ -378,6 +471,7 @@ private CosAction(
             this.mapper = new BuildArtifactMapper();
             this.currentListeners = new HashMap<>();
             this.cs = new ChangeSupport(this);
+            this.importantFilesCache = new AtomicReference<>(Pair.of(null,null));
             this.eval.addPropertyChangeListener(WeakListeners.propertyChange(this, this.eval));
             this.src.addPropertyChangeListener(WeakListeners.propertyChange(this, this.src));
             this.tests.addPropertyChangeListener(WeakListeners.propertyChange(this, this.tests));
@@ -421,20 +515,27 @@ public void artifactsUpdated(@NonNull final Iterable<File> artifacts) {
                 if (target != null) {
                     final FileObject buildXml = owner.findBuildXml();
                     if (buildXml != null) {
-                        try {
-                                ActionUtils.runTarget(
-                                    buildXml,
-                                    new String[] {target},
-                                    null,
-                                        null);
-                        } catch (IOException ioe) {
-                            LOG.log(
-                                    Level.WARNING,
-                                    "Cannot execute pos compile on save target: {0} in: {1}",   //NOI18N
-                                    new Object[]{
-                                        target,
-                                        FileUtil.getFileDisplayName(buildXml)
-                                    });
+                        if (checkImportantFiles(buildXml)) {
+                            RUNNER.execute(() -> {
+                                try {
+                                    final ExecutorTask task = runTargetInDedicatedTab(
+                                            NbBundle.getMessage(J2SEActionProvider.class, "LBL_CompileOnSaveUpdate"),
+                                            buildXml,
+                                            new String[] {target},
+                                            null,
+                                            null);
+                                    task.result();
+                                } catch (IOException | IllegalArgumentException ex) {
+                                    LOG.log(
+                                            Level.WARNING,
+                                            "Cannot execute pos compile on save target: {0} in: {1} due to: {2}",   //NOI18N
+                                            new Object[]{
+                                                target,
+                                                FileUtil.getFileDisplayName(buildXml),
+                                                ex.getMessage()
+                                            });
+                                }
+                            });
                         }
                     }
                 }
@@ -467,7 +568,42 @@ public void addChangeListener(@NonNull final ChangeListener listener) {
         @Override
         public void removeChangeListener(@NonNull final ChangeListener listener) {
             cs.removeChangeListener(listener);
-        }               
+        }
+
+        @Override
+        public void fileDeleted(FileEvent fe) {
+            resetImportantFilesCache();
+        }
+
+        @Override
+        public void fileChanged(FileEvent fe) {
+            resetImportantFilesCache();
+        }
+
+        @Override
+        public void fileFolderCreated(FileEvent fe) {
+            resetImportantFilesCache();
+        }
+
+        @Override
+        public void fileDataCreated(FileEvent fe) {
+            resetImportantFilesCache();
+        }
+
+        @Override
+        public void fileRenamed(FileRenameEvent fe) {
+            resetImportantFilesCache();
+        }
+
+        @Override
+        public void fileAttributeChanged(FileAttributeEvent fe) {
+            //Not important
+        }
+
+        @NonNull
+        Future<?> newSyncTask(@NonNull final Runnable callback) {
+            return RUNNER.submit(callback, null);
+        }
 
         private void updateRootsListeners() {
             final Set<URL> newRoots = new HashSet<>();
@@ -532,52 +668,55 @@ private Boolean performUpdate(@NonNull final Context ctx) {
             if (target != null) {
                 final FileObject buildXml = owner.findBuildXml();
                 if (buildXml != null) {
-                    try {
-                        final FileObject cosScript = getCosScript();
-                        final Iterable<? extends File> updated = ctx.getUpdated();
-                        final Iterable<? extends File> deleted = ctx.getDeleted();
-                        final File root = ctx.isCopyResources() ?
-                                BaseUtilities.toFile(ctx.getSourceRoot().toURI()) :
-                                ctx.getCacheRoot();
-                        final String includes = createIncludes(root, updated);
-                        if (includes != null) {
-                            final Properties props = new Properties();
-                            props.setProperty(PROP_TARGET, target);
-                            props.setProperty(PROP_SCRIPT, FileUtil.toFile(buildXml).getAbsolutePath());
-                            props.setProperty(PROP_SRCDIR, root.getAbsolutePath());
-                            props.setProperty(PROP_INCLUDES, includes);
-                            props.setProperty(COS_CUSTOM, getUpdatedFileSetProperty());
-                            RUNNER.execute(()-> {
-                                try {
-                                    final ExecutorTask task = ActionUtils.runTarget(
-                                            cosScript,
-                                            new String[] {TARGET},
-                                            props,
-                                            null);
-                                    task.result();
-                                } catch (IOException | IllegalArgumentException ex) {
-                                    LOG.log(
-                                        Level.WARNING,
-                                        "Cannot execute update targer: {0} in: {1} due to: {2}",   //NOI18N
-                                        new Object[]{
-                                            target,
-                                            FileUtil.getFileDisplayName(buildXml),
-                                            ex.getMessage()
-                                        });
-                                }
-                            });
-                        } else {
-                            LOG.warning("BuildArtifactMapper artifacts do not provide attributes.");    //NOI18N
-                        }
-                    } catch (IOException | URISyntaxException e) {
-                        LOG.log(
-                                Level.WARNING,
-                                "Cannot execute update targer: {0} in: {1} due to: {2}",   //NOI18N
-                                new Object[]{
-                                    target,
-                                    FileUtil.getFileDisplayName(buildXml),
-                                    e.getMessage()
+                    if (checkImportantFiles(buildXml)) {
+                        try {
+                            final FileObject cosScript = getCosScript();
+                            final Iterable<? extends File> updated = ctx.getUpdated();
+                            final Iterable<? extends File> deleted = ctx.getDeleted();
+                            final File root = ctx.isCopyResources() ?
+                                    BaseUtilities.toFile(ctx.getSourceRoot().toURI()) :
+                                    ctx.getCacheRoot();
+                            final String includes = createIncludes(root, updated);
+                            if (includes != null) {
+                                final Properties props = new Properties();
+                                props.setProperty(PROP_TARGET, target);
+                                props.setProperty(PROP_SCRIPT, FileUtil.toFile(buildXml).getAbsolutePath());
+                                props.setProperty(PROP_SRCDIR, root.getAbsolutePath());
+                                props.setProperty(PROP_INCLUDES, includes);
+                                props.setProperty(COS_CUSTOM, getUpdatedFileSetProperty());
+                                RUNNER.execute(()-> {
+                                    try {
+                                        final ExecutorTask task = runTargetInDedicatedTab(
+                                                NbBundle.getMessage(J2SEActionProvider.class, "LBL_CompileOnSaveUpdate"),
+                                                cosScript,
+                                                new String[] {TARGET},
+                                                props,
+                                                null);
+                                        task.result();
+                                    } catch (IOException | IllegalArgumentException ex) {
+                                        LOG.log(
+                                            Level.WARNING,
+                                            "Cannot execute update targer: {0} in: {1} due to: {2}",   //NOI18N
+                                            new Object[]{
+                                                target,
+                                                FileUtil.getFileDisplayName(buildXml),
+                                                ex.getMessage()
+                                            });
+                                    }
                                 });
+                            } else {
+                                LOG.warning("BuildArtifactMapper artifacts do not provide attributes.");    //NOI18N
+                            }
+                        } catch (IOException | URISyntaxException e) {
+                            LOG.log(
+                                    Level.WARNING,
+                                    "Cannot execute update targer: {0} in: {1} due to: {2}",   //NOI18N
+                                    new Object[]{
+                                        target,
+                                        FileUtil.getFileDisplayName(buildXml),
+                                        e.getMessage()
+                                    });
+                        }
                     }
                 }
             }
@@ -612,8 +751,113 @@ private FileObject getCosScript() throws IOException {
                 }
             }
             return cosScript;
-        }        
-        
+        }
+
+        private boolean checkImportantFiles(@NonNull final FileObject buildScript) {
+            final URI currentURI = buildScript.toURI();
+            final Pair<URI,Collection<File>> cacheLine = importantFilesCache.get();
+            final URI lastURI = cacheLine.first();
+            Collection<File> importantFiles = cacheLine.second();
+            if (!currentURI.equals(lastURI) || importantFiles == null) {
+                Optional.ofNullable(lastURI)
+                        .map(BaseUtilities::toFile)
+                        .filter((f) -> !currentURI.equals(lastURI))
+                        .ifPresent(this::safeRemoveFileChangeListener);
+                Optional.ofNullable(FileUtil.toFile(buildScript))
+                        .filter((f) -> !currentURI.equals(lastURI))
+                        .ifPresent(this::safeAddFileChangeListener);
+                importantFiles = new ArrayList<>();
+                try {
+                    final File base = FileUtil.toFile(buildScript.getParent());
+                    if (base != null) {
+                        Optional.ofNullable(DataObject.find(buildScript).getLookup().lookup(AntProjectCookie.class))
+                                .map(AntProjectCookie::getProjectElement)
+                                .map(XMLUtil::findSubElements)
+                                .map(Collection::stream)
+                                .orElse(Stream.empty())
+                                .filter((e) -> "import".equals(e.getNodeName()))    //NOI18N
+                                .map((e) -> e.getAttribute("file"))                 //NOI18N
+                                .filter((p) -> !p.isEmpty())
+                                .map((p) -> PropertyUtils.resolveFile(base, p))
+                                .forEach(importantFiles::add);
+                    }
+                } catch (DataObjectNotFoundException e) {
+                    LOG.log(
+                            Level.WARNING,
+                            "No DataObject for: {0}, reason: {1}",  //NOI18N
+                            new Object[]{
+                                FileUtil.getFileDisplayName(buildScript),
+                                e.getMessage()
+                            });
+                }
+                importantFilesCache.set(Pair.of(currentURI, importantFiles));
+            }
+            for (File importantFile : importantFiles) {
+                if (!importantFile.isFile()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private void resetImportantFilesCache() {
+            while (true) {
+                final Pair<URI, Collection<File>> expected = importantFilesCache.get();
+                final Pair<URI, Collection<File>> update = Pair.of(expected.first(), null);
+                if (importantFilesCache.compareAndSet(expected, update)) {
+                    break;
+                }
+            }
+        }
+
+        private void safeAddFileChangeListener(@NonNull final File f) {
+            try {
+                FileUtil.addFileChangeListener(this, f);
+            } catch (IllegalArgumentException e) {
+                //not important
+            }
+        }
+
+        private void safeRemoveFileChangeListener(@NonNull final File f) {
+            try {
+                FileUtil.removeFileChangeListener(this, f);
+            } catch (IllegalArgumentException e) {
+                //not important
+            }
+        }
+
+        @NonNull
+        private static ExecutorTask runTargetInDedicatedTab(
+                @NullAllowed final String tabName,
+                @NonNull final FileObject buildXml,
+                @NullAllowed final String[] targetNames,
+                @NullAllowed final Properties properties,
+                @NullAllowed final Set<String> concealedProperties) throws IOException, IllegalArgumentException {
+            Parameters.notNull("buildXml", buildXml);   //NOI18N
+            if (targetNames != null && targetNames.length == 0) {
+                throw new IllegalArgumentException("No targets supplied"); // NOI18N
+            }
+            final AntProjectCookie apc = AntScriptUtils.antProjectCookieFor(buildXml);
+            final AntTargetExecutor.Env execenv = new AntTargetExecutor.Env();
+            if (properties != null) {
+                Properties p = execenv.getProperties();
+                p.putAll(properties);
+                execenv.setProperties(p);
+            }
+            if (concealedProperties != null) {
+                execenv.setConcealedProperties(concealedProperties);
+            }
+            execenv.setSaveAllDocuments(false);
+            execenv.setPreferredName(tabName);
+            final Predicate<String> p = (s) -> tabName == null ?
+                    true :
+                    tabName.equals(s);
+            execenv.setTabReplaceStrategy(p, p);
+            execenv.setUserAction(false);
+            return AntTargetExecutor.createTargetExecutor(execenv)
+                    .execute(apc, targetNames);
+        }
+
         @CheckForNull
         private static String createIncludes(
                 @NonNull final File root,
diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java
index dbd33a43b..59c129c53 100644
--- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java
+++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java
@@ -66,6 +66,7 @@
 import org.netbeans.modules.java.api.common.ant.UpdateHelper;
 import org.netbeans.modules.java.api.common.classpath.ClassPathModifier;
 import org.netbeans.modules.java.api.common.classpath.ClassPathProviderImpl;
+import org.netbeans.modules.java.api.common.problems.ProjectProblemsProviders;
 import org.netbeans.modules.java.api.common.project.ProjectConfigurations;
 import org.netbeans.modules.java.api.common.project.ProjectHooks;
 import org.netbeans.modules.java.api.common.project.ProjectOperations;
@@ -370,9 +371,11 @@ public Boolean call() throws Exception {
             new J2SEProjectPlatformImpl(this),
             QuerySupport.createCompilerOptionsQuery(eval, ProjectProperties.JAVAC_COMPILERARGS),
             QuerySupport.createUnitTestsCompilerOptionsQuery(eval, sourceRoots, testRoots),
+            QuerySupport.createAutomaticModuleNameQuery(helper, eval, sourceRoots, ProjectProperties.MANIFEST_FILE),
             QuerySupport.createModuleInfoAccessibilityQuery(sourceRoots, testRoots),
             LookupMergerSupport.createCompilerOptionsQueryMerger(),
-            J2SEFileWizardIterator.create()
+            J2SEFileWizardIterator.create(),
+            ProjectProblemsProviders.createMissingModuleProjectProblemsProvider(this)
         );
         lookup = base; // in case LookupProvider's call Project.getLookup
         return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-java-j2seproject/Lookup"); //NOI18N
diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/api/J2SEProjectBuilder.java b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/api/J2SEProjectBuilder.java
index 703ce87a7..b401d4971 100644
--- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/api/J2SEProjectBuilder.java
+++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/api/J2SEProjectBuilder.java
@@ -376,7 +376,7 @@ public static void createDefaultModuleProperties(
             ep.setProperty(ProjectProperties.JAVAC_MODULEPATH, new String[0]);
         }
         if (ep.getProperty(ProjectProperties.JAVAC_PROCESSORMODULEPATH) == null) {
-            ep.setProperty(ProjectProperties.JAVAC_PROCESSORPATH, new String[0]);
+            ep.setProperty(ProjectProperties.JAVAC_PROCESSORMODULEPATH, new String[0]);
         }
         if (ep.getProperty(ProjectProperties.RUN_MODULEPATH) == null) {
             ep.setProperty(ProjectProperties.RUN_MODULEPATH, new String[] {
diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/resources/build-impl.xsl b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/resources/build-impl.xsl
index 8d9e8e1a6..395a993ec 100644
--- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/resources/build-impl.xsl
+++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/resources/build-impl.xsl
@@ -534,6 +534,16 @@ is divided into following sections:
                     <attribute>
                         <xsl:attribute name="name">sourcepath</xsl:attribute>
                         <xsl:attribute name="default">${empty.dir}</xsl:attribute>
+                        <xsl:attribute name="unless:set">named.module.internal</xsl:attribute>
+                    </attribute>
+                    <attribute>
+                        <xsl:attribute name="name">sourcepath</xsl:attribute>
+                        <xsl:attribute name="default">
+                            <xsl:call-template name="createPath">
+                                <xsl:with-param name="roots" select="/p:project/p:configuration/j2seproject3:data/j2seproject3:source-roots"/>
+                            </xsl:call-template>
+                        </xsl:attribute>
+                        <xsl:attribute name="if:set">named.module.internal</xsl:attribute>
                     </attribute>
                     <attribute>
                         <xsl:attribute name="name">gensrcdir</xsl:attribute>
@@ -544,11 +554,22 @@ is divided into following sections:
                         <xsl:attribute name="optional">true</xsl:attribute>
                     </element>
                     <sequential>
+                        <condition property="warn.excludes.internal">
+                            <and>
+                                <isset property="named.module.internal"/>
+                                <length string="@{{excludes}}" when="greater" length="0" trim="true"/>
+                            </and>
+                        </condition>
+                        <echo level="warning" message="The javac excludes are not supported in the JDK 9 Named Module." if:set="warn.excludes.internal"/>
                         <property name="empty.dir" location="${{build.dir}}/empty"/><!-- #157692 -->
                         <mkdir dir="${{empty.dir}}"/>
                         <mkdir dir="@{{apgeneratedsrcdir}}"/>
                         <condition property="processormodulepath.set">
-                            <length string="@{{toString:processormodulepath}}" when="greater" length="0"/>
+                            <resourcecount when="greater" count="0">
+                                <path>
+                                    <pathelement path="@{{processormodulepath}}"/>
+                                </path>
+                            </resourcecount>
                         </condition>
                         <javac>
                             <xsl:attribute name="srcdir">@{srcdir}</xsl:attribute>
@@ -2120,10 +2141,15 @@ is divided into following sections:
             </target>
 
             <target name ="-check-module-main-class" depends="init,compile">
+                <pathconvert property="main.class.file">
+                    <string value="${{main.class}}"/>
+                    <unpackagemapper from="*" to="*.class"/>
+                </pathconvert>
                 <condition property="do.module.main.class">
                     <and>
                         <isset property="main.class.available"/>
                         <available file="${{build.classes.dir}}/module-info.class"/>
+                        <available file="${{build.classes.dir}}/${{main.class.file}}"/>
                         <isset property="libs.CopyLibs.classpath"/>
                         <available classpath="${{libs.CopyLibs.classpath}}" classname="org.netbeans.modules.java.j2seproject.moduletask.ModuleMainClass"/>
                     </and>
@@ -2705,7 +2731,25 @@ is divided into following sections:
                             </xsl:call-template>
                     </xsl:attribute>
                 </j2seproject3:modulename>
-                <condition property="javac.test.compilerargs" value="--add-reads ${{test.module.name}}=ALL-UNNAMED" else="-Xmodule:${{module.name}} --add-reads ${{module.name}}=ALL-UNNAMED">
+                <condition>
+                    <xsl:attribute name="property">javac.test.sourcepath</xsl:attribute>
+                    <xsl:attribute name="value">
+                        <xsl:call-template name="createPath">
+                                <xsl:with-param name="roots" select="/p:project/p:configuration/j2seproject3:data/j2seproject3:test-roots"/>
+                        </xsl:call-template>
+                    </xsl:attribute>
+                    <xsl:attribute name="else">${empty.dir}</xsl:attribute>
+                    <and>
+                        <isset property="test.module.name"/>
+                        <length length="0" string="${{test.module.name}}" when="greater"/>
+                    </and>
+                </condition>
+                <condition>
+                    <xsl:attribute name="property">javac.test.compilerargs</xsl:attribute>
+                    <xsl:attribute name="value">--add-reads ${test.module.name}=ALL-UNNAMED</xsl:attribute>
+                    <xsl:attribute name="else">--patch-module ${module.name}=<xsl:call-template name="createPath">
+                                <xsl:with-param name="roots" select="/p:project/p:configuration/j2seproject3:data/j2seproject3:test-roots"/>
+                        </xsl:call-template> --add-reads ${module.name}=ALL-UNNAMED</xsl:attribute>
                     <and>
                         <isset property="test.module.name"/>
                         <length length="0" string="${{test.module.name}}" when="greater"/>
@@ -2740,6 +2784,7 @@ is divided into following sections:
                 </condition>
             </target>
             <target name="-init-test-module-properties-without-module" depends="-init-source-module-properties" unless="named.module.internal">
+                <property name="javac.test.sourcepath" value="${{empty.dir}}"/>
                 <property name="javac.test.compilerargs" value=""/>
                 <property name="run.test.jvmargs" value=""/>
             </target>
@@ -2771,6 +2816,7 @@ is divided into following sections:
                     <xsl:attribute name="processorpath">${javac.test.processorpath}</xsl:attribute>
                     <xsl:attribute name="modulepath">${javac.test.modulepath}</xsl:attribute>
                     <xsl:attribute name="apgeneratedsrcdir">${build.test.classes.dir}</xsl:attribute>
+                    <xsl:attribute name="sourcepath">${javac.test.sourcepath}</xsl:attribute>
                     <customize>
                         <compilerarg line="${{javac.test.compilerargs}}"/>
                     </customize>
diff --git a/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java b/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java
index a5efcf33f..567703300 100644
--- a/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java
+++ b/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java
@@ -128,6 +128,7 @@ private ClasspathInfo(final @NonNull ClassPath bootCp,
                           final boolean ignoreExcludes,
                           final boolean hasMemoryFileManager,
                           final boolean useModifiedFiles,
+                          final boolean requiresSourceRoots,
                           @NullAllowed final Function<JavaFileManager.Location, JavaFileManager> jfmProvider) {
         assert bootCp != null;
         assert compileCp != null;
@@ -159,6 +160,9 @@ private ClasspathInfo(final @NonNull ClassPath bootCp,
                 this.cachedSrcClassPath.addPropertyChangeListener(WeakListeners.propertyChange(this.cpListener,this.cachedSrcClassPath));
             }
         }
+        if (requiresSourceRoots && this.cachedSrcClassPath.entries().isEmpty()) {
+            throw new OutputFileManager.InvalidSourcePath();
+        }
         if (moduleSrcCp == null) {
             this.moduleSrcPath = ClassPath.EMPTY;
         } else {
@@ -387,6 +391,7 @@ public ClasspathInfo build() {
                     false,
                     false,
                     true,
+                    false,
                     null);
         }
     }
@@ -422,7 +427,7 @@ private static ClasspathInfo create (
         }
         ClassPath srcPath = ClassPath.getClassPath(fo, ClassPath.SOURCE);
         ClassPath moduleSrcPath = ClassPath.getClassPath(fo, JavaClassPathConstants.MODULE_SOURCE_PATH);
-        return create (bootPath, moduleBootPath, compilePath, moduleCompilePath, moduleClassPath, srcPath, moduleSrcPath, filter, backgroundCompilation, ignoreExcludes, hasMemoryFileManager, useModifiedFiles, null);
+        return create (bootPath, moduleBootPath, compilePath, moduleCompilePath, moduleClassPath, srcPath, moduleSrcPath, filter, backgroundCompilation, ignoreExcludes, hasMemoryFileManager, useModifiedFiles, false, null);
     }
 
     @NonNull
@@ -439,9 +444,23 @@ private static ClasspathInfo create(
             final boolean ignoreExcludes,
             final boolean hasMemoryFileManager,
             final boolean useModifiedFiles,
+            final boolean requiresSourceRoots,
             @NullAllowed final Function<JavaFileManager.Location, JavaFileManager> jfmProvider) {
-        return new ClasspathInfo(bootPath, moduleBootPath, classPath, moduleCompilePath, moduleClassPath, sourcePath, moduleSourcePath,
-                filter, backgroundCompilation, ignoreExcludes, hasMemoryFileManager, useModifiedFiles, jfmProvider);
+        return new ClasspathInfo(
+                bootPath,
+                moduleBootPath,
+                classPath,
+                moduleCompilePath,
+                moduleClassPath,
+                sourcePath,
+                moduleSourcePath,
+                filter,
+                backgroundCompilation,
+                ignoreExcludes,
+                hasMemoryFileManager,
+                useModifiedFiles,
+                requiresSourceRoots,
+                jfmProvider);
     }
 
     // Public methods ----------------------------------------------------------
@@ -682,8 +701,23 @@ public ClasspathInfo create (
                 final boolean ignoreExcludes,
                 final boolean hasMemoryFileManager,
                 final boolean useModifiedFiles,
+                final boolean requiresSourceRoots,
                 @NullAllowed final Function<JavaFileManager.Location, JavaFileManager> jfmProvider) {
-            return ClasspathInfo.create(bootPath, moduleBootPath, classPath, moduleCompilePath, moduleClassPath, sourcePath, moduleSourcePath, filter, backgroundCompilation, ignoreExcludes, hasMemoryFileManager, useModifiedFiles, jfmProvider);
+            return ClasspathInfo.create(
+                    bootPath,
+                    moduleBootPath,
+                    classPath,
+                    moduleCompilePath,
+                    moduleClassPath,
+                    sourcePath,
+                    moduleSourcePath,
+                    filter,
+                    backgroundCompilation,
+                    ignoreExcludes,
+                    hasMemoryFileManager,
+                    useModifiedFiles,
+                    requiresSourceRoots,
+                    jfmProvider);
         }
 
         @Override
diff --git a/java.source.base/src/org/netbeans/api/java/source/JavaSource.java b/java.source.base/src/org/netbeans/api/java/source/JavaSource.java
index 34b82b6bf..a881a5387 100644
--- a/java.source.base/src/org/netbeans/api/java/source/JavaSource.java
+++ b/java.source.base/src/org/netbeans/api/java/source/JavaSource.java
@@ -286,7 +286,7 @@ public FileObject getFile () {
                     srcPath,
                     moduleSrcPath,
                     null,
-                    false, false, false, true, null);
+                    false, false, false, true, false, null);
                 FileObject root = ClassPathSupport.createProxyClassPath(
                     ClassPathSupport.createClassPath(CachingArchiveProvider.getDefault().ctSymRootsFor(bootPath)),
                     bootPath,
diff --git a/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java b/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java
index 10016b672..747fa54c0 100644
--- a/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java
+++ b/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java
@@ -60,10 +60,7 @@
 import com.sun.tools.javac.model.JavacElements;
 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
 import com.sun.tools.javac.util.Context;
-import java.io.BufferedInputStream;
-import java.io.InputStream;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 import javax.swing.SwingUtilities;
 import javax.tools.JavaFileManager;
@@ -78,7 +75,6 @@
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.java.classpath.JavaClassPathConstants;
 import org.netbeans.api.java.lexer.JavaTokenId;
-import org.netbeans.api.java.queries.BinaryForSourceQuery;
 import org.netbeans.api.java.queries.JavadocForBinaryQuery;
 import org.netbeans.api.java.queries.SourceForBinaryQuery;
 import org.netbeans.api.java.source.ClasspathInfo.PathKind;
@@ -88,15 +84,12 @@
 import org.netbeans.api.java.source.matching.Pattern;
 import org.netbeans.api.lexer.TokenHierarchy;
 import org.netbeans.api.lexer.TokenSequence;
-import org.netbeans.api.queries.FileEncodingQuery;
-import org.netbeans.modules.classfile.ClassFile;
-import org.netbeans.modules.classfile.Module;
 import org.netbeans.modules.java.preprocessorbridge.spi.ImportProcessor;
 import org.netbeans.modules.java.source.ElementHandleAccessor;
 import org.netbeans.modules.java.source.JavadocHelper;
+import org.netbeans.modules.java.source.ModuleNames;
 import org.netbeans.modules.java.source.indexing.FQN2Files;
 import org.netbeans.modules.java.source.indexing.JavaCustomIndexer;
-import org.netbeans.modules.java.source.indexing.JavaIndex;
 import org.netbeans.modules.java.source.parsing.ClasspathInfoProvider;
 import org.netbeans.modules.java.source.parsing.FileObjects;
 import org.netbeans.modules.java.source.parsing.Hacks;
@@ -128,7 +121,6 @@
  */
 public class SourceUtils {
 
-    private static final java.util.regex.Pattern AUTO_NAME_PATTERN = java.util.regex.Pattern.compile("-(\\d+(\\.|$))"); //NOI18N
     private static final Logger LOG = Logger.getLogger(SourceUtils.class.getName());
 
     private SourceUtils() {}
@@ -1284,60 +1276,7 @@ public static String getModuleName(@NonNull final URL rootUrl) {
     public static String getModuleName(
             @NonNull final URL rootUrl,
             @NonNull final boolean canUseSources) {
-        if (FileObjects.PROTO_NBJRT.equals(rootUrl.getProtocol())) {
-            //Platform
-            final String path = rootUrl.getPath();
-            int endIndex = path.length() - 1;
-            int startIndex = path.lastIndexOf('/', endIndex - 1);   //NOI18N
-            return path.substring(startIndex+1, endIndex);
-        }
-        final URL srcRootURL = JavaIndex.getSourceRootForClassFolder(rootUrl);
-        if (srcRootURL != null) {
-            //Cache folder
-            return getProjectModuleName(Collections.singletonList(srcRootURL), canUseSources);
-        }
-        final SourceForBinaryQuery.Result2 sfbqRes = SourceForBinaryQuery.findSourceRoots2(rootUrl);
-        if (sfbqRes.preferSources()) {
-            //Project binary
-            final String moduleName = getProjectModuleName(
-                    Arrays.stream(sfbqRes.getRoots()).map(FileObject::toURL).collect(Collectors.toList()),
-                    canUseSources);
-            if (moduleName != null) {
-                return moduleName;
-            }
-        }
-        //Binary
-        if (FileUtil.isArchiveArtifact(rootUrl)) {
-            //Archive
-            final FileObject root = URLMapper.findFileObject(rootUrl);
-            if (root != null) {
-                final FileObject moduleInfo = root.getFileObject(FileObjects.MODULE_INFO, FileObjects.CLASS);
-                if (moduleInfo != null) {
-                    try {
-                        return readModuleName(moduleInfo);
-                    } catch (IOException ioe) {
-                        //Behave as javac: Pass to automatic module
-                    }
-                }
-                //Automatic module
-                final FileObject file = FileUtil.getArchiveFile(root);
-                if (file != null) {
-                    return autoName(file.getName());
-                }
-            }
-        } else {
-            //Regular module folder//Folder
-            final FileObject root = URLMapper.findFileObject(rootUrl);
-            FileObject moduleInfo;
-            if (root != null && (moduleInfo = root.getFileObject(FileObjects.MODULE_INFO, FileObjects.CLASS)) != null) {
-                try {
-                    return readModuleName(moduleInfo);
-                } catch (IOException ioe) {
-                    //pass to null
-                }
-            }
-        }
-        return null;
+        return ModuleNames.getInstance().getModuleName(rootUrl, canUseSources);
     }
 
     /**
@@ -1349,33 +1288,7 @@ public static String getModuleName(
     @CheckForNull
     public static String parseModuleName(
             @NonNull final FileObject moduleInfo) {
-        final JavacTaskImpl jt = JavacParser.createJavacTask(
-                new ClasspathInfo.Builder(ClassPath.EMPTY).build(),
-                null,
-                "1.3",  //min sl to prevent validateSourceLevel warning
-                null,
-                null,
-                null,
-                null,
-                null,
-                null);
-        try {
-            final CompilationUnitTree cu =  jt.parse(FileObjects.fileObjectFileObject(
-                    moduleInfo,
-                    moduleInfo.getParent(),
-                    null,
-                    FileEncodingQuery.getEncoding(moduleInfo))).iterator().next();
-            final List<? extends Tree> typeDecls = cu.getTypeDecls();
-            if (!typeDecls.isEmpty()) {
-                final Tree typeDecl = typeDecls.get(0);
-                if (typeDecl.getKind() == Tree.Kind.MODULE) {
-                    return ((ModuleTree)typeDecl).getName().toString();
-                }
-            }
-        } catch (IOException ioe) {
-            Exceptions.printStackTrace(ioe);
-        }
-        return null;
+        return ModuleNames.parseModuleName(moduleInfo);
     }
 
     // --------------- Helper methods of getFile () -----------------------------
@@ -1612,93 +1525,6 @@ private static String tryToMakeAcronym (String s) {
         return res;
     }
 
-    @CheckForNull
-    private static String getProjectModuleName(
-            @NonNull final List<URL> srcRootURLs,
-            final boolean canUseSources) {
-        if (srcRootURLs.isEmpty()) {
-            return null;
-        }
-        if (srcRootURLs.stream().allMatch((srcRootURL)->JavaIndex.hasSourceCache(srcRootURL,false))) {
-            //scanned
-            String moduleName = null;
-            for (URL srcRootURL : srcRootURLs) {
-                try {
-                    moduleName = JavaIndex.getAttribute(srcRootURL, JavaIndex.ATTR_MODULE_NAME, null);
-                    if (moduleName != null) {
-                        break;
-                    }
-                } catch (IOException ioe) {
-                    Exceptions.printStackTrace(ioe);
-                }
-            }
-            if (moduleName != null) {
-                //Has module-info
-                return moduleName;
-            }
-            //No module -> automatic module
-            return autoName(srcRootURLs);
-        } else if (canUseSources) {
-            FileObject moduleInfo = null;
-            FileObject root = null;
-            for (URL srcRootUrl : srcRootURLs) {
-                final FileObject srcRoot = URLMapper.findFileObject(srcRootUrl);
-                if (srcRoot != null) {
-                    moduleInfo = srcRoot.getFileObject(FileObjects.MODULE_INFO, FileObjects.JAVA);
-                    if (moduleInfo != null) {
-                        root = srcRoot;
-                        break;
-                    }
-                }
-            }
-            if (moduleInfo != null) {
-                return parseModuleName(moduleInfo);
-            } else {
-                //No module -> automatic module
-                return autoName(srcRootURLs);
-            }
-        }
-        return null;
-    }
-
-    @CheckForNull
-    private static String autoName(@NonNull final List<? extends URL> srcRootURLs) {
-        for (URL binRoot : BinaryForSourceQuery.findBinaryRoots(srcRootURLs.get(0)).getRoots()) {
-            if (FileObjects.JAR.equals(binRoot.getProtocol())) {
-                return autoName(FileObjects.stripExtension(FileUtil.archiveOrDirForURL(binRoot).getName()));
-            }
-        }
-        return null;
-    }
-
-    @CheckForNull
-    private static String autoName(@NonNull String moduleName) {
-        final java.util.regex.Matcher matcher = AUTO_NAME_PATTERN.matcher(moduleName);
-        if (matcher.find()) {
-            int start = matcher.start();
-            moduleName = moduleName.substring(0, start);
-        }
-        moduleName =  moduleName
-            .replaceAll("(\\.|\\d)*$", "")    // remove trailing version
-            .replaceAll("[^A-Za-z0-9]", ".")  // replace non-alphanumeric
-            .replaceAll("(\\.)(\\1)+", ".")   // collapse repeating dots
-            .replaceAll("^\\.", "")           // drop leading dots
-            .replaceAll("\\.$", "");          // drop trailing dots
-        return moduleName.isEmpty() ?
-            null :
-            moduleName;
-    }
-
-    @CheckForNull
-    private static String readModuleName(@NonNull FileObject moduleInfo) throws IOException {
-        try (final InputStream in = new BufferedInputStream(moduleInfo.getInputStream())) {
-            final ClassFile clz = new ClassFile(in, false);
-            final Module modle = clz.getModule();
-            return modle != null ?
-                    modle.getName() :
-                    null;
-        }
-    }
 
     private static boolean isPkgOrMdl(@NonNull final ElementKind kind) {
         return kind == ElementKind.PACKAGE || kind == ElementKind.MODULE;
diff --git a/java.source.base/src/org/netbeans/modules/java/source/JavaSourceUtilImpl.java b/java.source.base/src/org/netbeans/modules/java/source/JavaSourceUtilImpl.java
index 2f9cdcc6a..8fa251105 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/JavaSourceUtilImpl.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/JavaSourceUtilImpl.java
@@ -211,6 +211,7 @@ protected long createTaggedCompilationController(FileObject file, long currenTag
                     true,
                     false,
                     false,
+                    false,
                     jfmProvider);
             final APTUtils aptUtils = APTUtils.get(srcRoot);
             final JavacTaskImpl  jt = JavacParser.createJavacTask(
diff --git a/java.source.base/src/org/netbeans/modules/java/source/ModuleNames.java b/java.source.base/src/org/netbeans/modules/java/source/ModuleNames.java
new file mode 100644
index 000000000..c87a2b6b8
--- /dev/null
+++ b/java.source.base/src/org/netbeans/modules/java/source/ModuleNames.java
@@ -0,0 +1,561 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ModuleTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.queries.BinaryForSourceQuery;
+import org.netbeans.api.java.queries.CompilerOptionsQuery;
+import org.netbeans.api.java.queries.SourceForBinaryQuery;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.queries.FileEncodingQuery;
+import org.netbeans.modules.classfile.ClassFile;
+import org.netbeans.modules.classfile.Module;
+import org.netbeans.modules.java.source.indexing.JavaIndex;
+import org.netbeans.modules.java.source.parsing.FileObjects;
+import org.netbeans.modules.java.source.parsing.JavacParser;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileRenameEvent;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.WeakListeners;
+
+/**
+ * Computes module names.
+ * Computes and caches module names.
+ * @author Tomas Zezula
+ */
+public final class ModuleNames {
+    private static final Logger LOG = Logger.getLogger(ModuleNames.class.getName());
+    private static final java.util.regex.Pattern AUTO_NAME_PATTERN = java.util.regex.Pattern.compile("-(\\d+(\\.|$))"); //NOI18N
+    private static final String RES_MANIFEST = "META-INF/MANIFEST.MF";              //NOI18N
+    private static final String ATTR_AUTOMATIC_MOD_NAME = "Automatic-Module-Name";   //NOI18N
+    private static final Pattern AUTOMATIC_MODULE_NAME_MATCHER = Pattern.compile("-XDautomatic-module-name:(.*)");  //NOI18N
+    private static final ModuleNames INSTANCE = new ModuleNames();
+
+    private final Map<URL,CacheLine> cache;
+
+    private ModuleNames() {
+        this.cache = new ConcurrentHashMap<>();
+    }
+
+    @CheckForNull
+    public String getModuleName(
+            @NonNull final URL rootUrl,
+            final boolean canUseSources) {
+        try {
+            final CacheLine cl = cache.get(rootUrl);
+            if (cl != null) {
+                return cl.getValue();
+            }
+        } catch (InvalidCacheLine icl) {
+            //pass and recompute
+        }
+        LOG.log(Level.FINE, "No cache for: {0}", rootUrl);
+        if (FileObjects.PROTO_NBJRT.equals(rootUrl.getProtocol())) {
+            //Platform
+            final String path = rootUrl.getPath();
+            int endIndex = path.length() - 1;
+            int startIndex = path.lastIndexOf('/', endIndex - 1);   //NOI18N
+            return register(
+                    rootUrl,
+                    new CacheLine(rootUrl, path.substring(startIndex+1, endIndex)));
+        }
+        final URL srcRootURL = JavaIndex.getSourceRootForClassFolder(rootUrl);
+        if (srcRootURL != null) {
+            //Cache folder
+            return register(rootUrl, getProjectModuleName(rootUrl, Collections.singletonList(srcRootURL), canUseSources));
+        }
+        final SourceForBinaryQuery.Result2 sfbqRes = SourceForBinaryQuery.findSourceRoots2(rootUrl);
+        if (sfbqRes.preferSources()) {
+            //Project binary
+            final CacheLine cl = getProjectModuleName(
+                    rootUrl,
+                    Arrays.stream(sfbqRes.getRoots()).map(FileObject::toURL).collect(Collectors.toList()),
+                    canUseSources);
+            if (cl.getValueNoCheck() != null) {
+                return register(rootUrl, cl);
+            }
+        }
+        //Binary
+        if (FileUtil.isArchiveArtifact(rootUrl)) {
+            //Archive
+            final FileObject root = URLMapper.findFileObject(rootUrl);
+            if (root != null) {
+                final FileObject file = FileUtil.getArchiveFile(root);
+                final FileObject moduleInfo = root.getFileObject(FileObjects.MODULE_INFO, FileObjects.CLASS);
+                if (moduleInfo != null) {
+                    try {
+                        final String modName = readModuleName(moduleInfo);
+                        final File path = Optional.ofNullable(file)
+                                .map(FileUtil::toFile)
+                                .orElse(null);
+                        return register(
+                                rootUrl,
+                                path != null ?
+                                    new FileCacheLine(rootUrl, modName, path):
+                                    new FileObjectCacheLine(rootUrl, modName, moduleInfo));
+                    } catch (IOException ioe) {
+                        //Behave as javac: Pass to automatic module
+                    }
+                }
+                final FileObject manifest = root.getFileObject(RES_MANIFEST);
+                if (manifest != null) {
+                    try {
+                        try (final InputStream in = new BufferedInputStream(manifest.getInputStream())) {
+                            final Manifest mf = new Manifest(in);
+                            final String autoModName = mf.getMainAttributes().getValue(ATTR_AUTOMATIC_MOD_NAME);
+                            if (autoModName != null) {
+                                final File path = Optional.ofNullable(file)
+                                        .map(FileUtil::toFile)
+                                        .orElse(null);
+                                return register(
+                                    rootUrl,
+                                    path != null ?
+                                        new FileCacheLine(rootUrl, autoModName, path):
+                                        new FileObjectCacheLine(
+                                                rootUrl,
+                                                autoModName,
+                                                file != null ?
+                                                        file :
+                                                        manifest));
+                            }
+                        }
+                    } catch (IOException ioe) {
+                        //Behave as javac: Pass to automatic module
+                    }
+                }
+                //Automatic module
+                if (file != null) {
+                    final String modName = autoName(file.getName());
+                    final File path = FileUtil.toFile(file);
+                    return register(
+                            rootUrl,
+                            path != null ?
+                                new FileCacheLine(rootUrl, modName, path):
+                                new FileObjectCacheLine(rootUrl, modName, file));
+                }
+            }
+        } else {
+            //Regular module folder or folder
+            final FileObject root = URLMapper.findFileObject(rootUrl);
+            FileObject moduleInfo;
+            if (root != null && (moduleInfo = root.getFileObject(FileObjects.MODULE_INFO, FileObjects.CLASS)) != null) {
+                try {
+                    final String modName = readModuleName(moduleInfo);
+                    final File path = FileUtil.toFile(moduleInfo);
+                    return register(
+                            rootUrl,
+                            path != null ?
+                                    new FileCacheLine(rootUrl, modName, path):
+                                    new FileObjectCacheLine(rootUrl, modName, moduleInfo));
+                } catch (IOException ioe) {
+                    //pass to null
+                }
+            }
+        }
+        return null;
+    }
+
+    public void reset(@NonNull final URL binRootURL) {
+        Optional.ofNullable(cache.get(binRootURL))
+                .ifPresent(CacheLine::invalidate);
+    }
+
+    private String register(
+            @NonNull final URL rootUrl,
+            @NonNull final CacheLine cacheLine) {
+            cache.put(rootUrl, cacheLine);
+            return cacheLine.getValueNoCheck();
+    }
+
+    @NonNull
+    private static CacheLine getProjectModuleName(
+            @NonNull final URL artefact,
+            @NonNull final List<URL> srcRootURLs,
+            final boolean canUseSources) {
+        if (srcRootURLs.isEmpty()) {
+            return new CacheLine(artefact, null);
+        }
+        if (srcRootURLs.stream().allMatch((srcRootURL)->JavaIndex.hasSourceCache(srcRootURL,false))) {
+            //scanned
+            String modName = null;
+            for (URL srcRootURL : srcRootURLs) {
+                try {
+                    modName = JavaIndex.getAttribute(srcRootURL, JavaIndex.ATTR_MODULE_NAME, null);
+                    if (modName != null) {
+                        break;
+                    }
+                } catch (IOException ioe) {
+                    Exceptions.printStackTrace(ioe);
+                }
+            }
+            if (modName != null) {
+                //Has module-info
+                return new CacheLine(artefact, modName);
+            }
+            //No module -> automatic module
+            return autoName(artefact, srcRootURLs);
+        } else if (canUseSources) {
+            FileObject moduleInfo = null;
+            FileObject root = null;
+            for (URL srcRootUrl : srcRootURLs) {
+                final FileObject srcRoot = URLMapper.findFileObject(srcRootUrl);
+                if (srcRoot != null) {
+                    moduleInfo = srcRoot.getFileObject(FileObjects.MODULE_INFO, FileObjects.JAVA);
+                    if (moduleInfo != null) {
+                        root = srcRoot;
+                        break;
+                    }
+                }
+            }
+            if (moduleInfo != null) {
+                final String modName = parseModuleName(moduleInfo);
+                final File path = FileUtil.toFile(moduleInfo);
+                return path != null ?
+                        new FileCacheLine(artefact, modName, path):
+                        new FileObjectCacheLine(artefact, modName, moduleInfo);
+            } else {
+                //No module -> automatic module
+                return autoName(artefact, srcRootURLs);
+            }
+        }
+        return new CacheLine(artefact, null);
+    }
+
+    @CheckForNull
+    public static String parseModuleName(
+            @NonNull final FileObject moduleInfo) {
+        final JavacTaskImpl jt = JavacParser.createJavacTask(
+                new ClasspathInfo.Builder(ClassPath.EMPTY).build(),
+                null,
+                "1.3",  //min sl to prevent validateSourceLevel warning
+                null,
+                null,
+                null,
+                null,
+                null,
+                null);
+        try {
+            final CompilationUnitTree cu =  jt.parse(FileObjects.fileObjectFileObject(
+                    moduleInfo,
+                    moduleInfo.getParent(),
+                    null,
+                    FileEncodingQuery.getEncoding(moduleInfo))).iterator().next();
+            final List<? extends Tree> typeDecls = cu.getTypeDecls();
+            if (!typeDecls.isEmpty()) {
+                final Tree typeDecl = typeDecls.get(0);
+                if (typeDecl.getKind() == Tree.Kind.MODULE) {
+                    return ((ModuleTree)typeDecl).getName().toString();
+                }
+            }
+        } catch (IOException ioe) {
+            Exceptions.printStackTrace(ioe);
+        }
+        return null;
+    }
+
+    @NonNull
+    private static CacheLine autoName(
+            @NonNull final URL artefact,
+            @NonNull final List<? extends URL> srcRootURLs) {
+        CompilerOptionsQuery.Result cops = null;
+        String amn = null;
+        for (URL srcRootURL : srcRootURLs) {
+            final FileObject fo = URLMapper.findFileObject(srcRootURL);
+            if (fo != null) {
+                cops  = CompilerOptionsQuery.getOptions(fo);
+                for (String opt : cops.getArguments()) {
+                    final Matcher m = AUTOMATIC_MODULE_NAME_MATCHER.matcher(opt);
+                    if (m.matches()) {
+                        amn = m.group(1);
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+        if (amn != null) {
+            return new BinCacheLine(artefact, amn, cops, null);
+        }
+        final BinaryForSourceQuery.Result res = BinaryForSourceQuery.findBinaryRoots(srcRootURLs.get(0));
+        for (URL binRoot : res.getRoots()) {
+            if (FileObjects.JAR.equals(binRoot.getProtocol())) {
+                final String modName = autoName(FileObjects.stripExtension(FileUtil.archiveOrDirForURL(binRoot).getName()));
+                return new BinCacheLine(artefact, modName, cops, res);
+            }
+        }
+        return new CacheLine(artefact, null);
+    }
+
+    @CheckForNull
+    private static String autoName(@NonNull String moduleName) {
+        final java.util.regex.Matcher matcher = AUTO_NAME_PATTERN.matcher(moduleName);
+        if (matcher.find()) {
+            int start = matcher.start();
+            moduleName = moduleName.substring(0, start);
+        }
+        moduleName =  moduleName
+            .replaceAll("(\\.|\\d)*$", "")    // remove trailing version
+            .replaceAll("[^A-Za-z0-9]", ".")  // replace non-alphanumeric
+            .replaceAll("(\\.)(\\1)+", ".")   // collapse repeating dots
+            .replaceAll("^\\.", "")           // drop leading dots
+            .replaceAll("\\.$", "");          // drop trailing dots
+        return moduleName.isEmpty() ?
+            null :
+            moduleName;
+    }
+
+    @CheckForNull
+    private static String readModuleName(@NonNull FileObject moduleInfo) throws IOException {
+        try (final InputStream in = new BufferedInputStream(moduleInfo.getInputStream())) {
+            final ClassFile clz = new ClassFile(in, false);
+            final Module modle = clz.getModule();
+            return modle != null ?
+                    modle.getName() :
+                    null;
+        }
+    }
+
+
+    @NonNull
+    public static ModuleNames getInstance() {
+        return INSTANCE;
+    }
+
+    private static final class InvalidCacheLine extends Exception {
+        static final InvalidCacheLine INSTANCE = new InvalidCacheLine();
+
+        private InvalidCacheLine() {
+        }
+
+        @Override
+        public synchronized Throwable fillInStackTrace() {
+            return this;
+        }
+    }
+
+    private static class CacheLine {
+        private final URL artefact;
+        private final String value;
+        private volatile boolean  invalid;
+
+        CacheLine(
+                @NonNull final URL artefact,
+                @NullAllowed final String value) {
+            this.artefact = artefact;
+            this.value = value;
+            this.invalid = false;
+        }
+
+        @CheckForNull
+        final String getValue() throws InvalidCacheLine {
+            if (invalid) {
+                throw InvalidCacheLine.INSTANCE;
+            } else {
+                return value;
+            }
+        }
+
+        @CheckForNull
+        final String getValueNoCheck() {
+            return value;
+        }
+
+        void invalidate() {
+            LOG.log(Level.FINE, "Invalidated cache for: {0}", artefact);
+            this.invalid = true;
+        }
+    }
+
+    private static final class FileCacheLine extends CacheLine implements FileChangeListener {
+        private final File path;
+        private final AtomicBoolean listens = new AtomicBoolean(true);
+        FileCacheLine(
+                @NonNull final URL artefact,
+                @NullAllowed final String modName,
+                @NonNull final File path) {
+            super(artefact, modName);
+            this.path = path;
+            FileUtil.addFileChangeListener(this, path);
+        }
+
+        @Override
+        public void fileFolderCreated(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileDataCreated(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileChanged(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileDeleted(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileRenamed(FileRenameEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileAttributeChanged(FileAttributeEvent fe) {
+        }
+
+        @Override
+        void invalidate() {
+            super.invalidate();
+            if (listens.compareAndSet(true, false)) {
+                FileUtil.removeFileChangeListener(this, path);
+            }
+        }
+    }
+
+    private static final class FileObjectCacheLine extends CacheLine implements FileChangeListener {
+        private final FileObject file;
+        private final FileChangeListener wl;
+
+        FileObjectCacheLine(
+                @NonNull final URL artefact,
+                @NullAllowed final String modName,
+                @NonNull final FileObject file) {
+            super(artefact, modName);
+            this.file = file;
+            this.wl = FileUtil.weakFileChangeListener(this, this.file);
+            this.file.addFileChangeListener(this.wl);
+        }
+
+        @Override
+        public void fileFolderCreated(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileDataCreated(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileChanged(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileDeleted(FileEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileRenamed(FileRenameEvent fe) {
+            invalidate();
+        }
+
+        @Override
+        public void fileAttributeChanged(FileAttributeEvent fe) {
+        }
+
+        @Override
+        void invalidate() {
+            super.invalidate();
+            this.file.removeFileChangeListener(this.wl);
+        }
+    }
+
+    private static final class BinCacheLine extends CacheLine implements ChangeListener {
+        private final CompilerOptionsQuery.Result cops;
+        private final BinaryForSourceQuery.Result res;
+        private final ChangeListener copsCl;
+        private final ChangeListener resCl;
+
+        BinCacheLine(
+                @NonNull final URL artefact,
+                @NonNull final String modName,
+                @NullAllowed final CompilerOptionsQuery.Result cops,
+                @NullAllowed final BinaryForSourceQuery.Result res) {
+            super(artefact, modName);
+            this.cops = cops;
+            this.res =  res;
+            if (this.cops != null) {
+                this.copsCl = WeakListeners.change(this, this.cops);
+                this.cops.addChangeListener(copsCl);
+            } else {
+                this.copsCl = null;
+            }
+            if (this.res != null) {
+                this.resCl = WeakListeners.change(this, this.res);
+                this.res.addChangeListener(resCl);
+            } else {
+                this.resCl = null;
+            }
+        }
+
+        @Override
+        public void stateChanged(ChangeEvent e) {
+            invalidate();
+        }
+
+        @Override
+        void invalidate() {
+            super.invalidate();
+            if (this.copsCl != null) {
+                this.cops.removeChangeListener(this.copsCl);;
+            }
+            if (this.resCl != null) {
+                this.res.removeChangeListener(this.resCl);
+            }
+        }
+    }
+}
diff --git a/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java b/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java
index 904150109..c72438d1e 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java
@@ -233,6 +233,15 @@ private static boolean isAptBuildGeneratedFolder(@NonNull final FileObject root)
         return false;
     }
 
+    /**
+     * Returns the root the APTUtils was created for.
+     * @return the {@link FileObject}
+     */
+    @NonNull
+    public FileObject getRoot() {
+        return root;
+    }
+
     public boolean aptEnabledOnScan() {
         return aptOptions.annotationProcessingEnabled().contains(AnnotationProcessingQuery.Trigger.ON_SCAN);
     }
diff --git a/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java b/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java
index b70a3546a..fd1c855bd 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java
@@ -190,6 +190,7 @@ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
             true,
             false,
             false,
+            false,
             null);
         final JavacTaskImpl jt = JavacParser.createJavacTask(cpInfo, new DevNullDiagnosticListener(), null, null, null, null, null, null, null);
         TreeLoader.preRegister(jt.getContext(), cpInfo, true);
diff --git a/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java b/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java
index 6eb2075bc..25e6bbff6 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java
@@ -70,14 +70,15 @@
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.java.classpath.JavaClassPathConstants;
 import org.netbeans.api.java.queries.AnnotationProcessingQuery;
+import org.netbeans.api.java.queries.BinaryForSourceQuery;
 import org.netbeans.api.java.source.ClassIndex;
 import org.netbeans.api.java.source.ClasspathInfo;
 import org.netbeans.api.java.source.ElementHandle;
 import org.netbeans.api.project.FileOwnerQuery;
 import org.netbeans.api.project.Project;
-import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.modules.java.source.ElementHandleAccessor;
 import org.netbeans.modules.java.source.JavaSourceTaskFactoryManager;
+import org.netbeans.modules.java.source.ModuleNames;
 import org.netbeans.modules.java.source.base.Module;
 import org.netbeans.modules.java.source.parsing.FileManagerTransaction;
 import org.netbeans.modules.java.source.parsing.FileObjects;
@@ -202,7 +203,16 @@ protected void index(final Iterable<? extends Indexable> files, final Context co
                 final JavaParsingContext javaContext;
                 try {
                     //todo: Ugly hack, the ClassIndexManager.createUsagesQuery has to be called before the root is set to dirty mode.
-                    javaContext = new JavaParsingContext(context, bootPath, moduleBootPath != null ? moduleBootPath : ClassPath.EMPTY, compilePath, moduleCompilePath != null ? moduleCompilePath : ClassPath.EMPTY, moduleClassPath != null ? moduleClassPath : ClassPath.EMPTY, sourcePath, moduleSourcePath, virtualSourceTuples);
+                    javaContext = new JavaParsingContext(
+                            context,
+                            bootPath,
+                            moduleBootPath != null ? moduleBootPath : ClassPath.EMPTY,
+                            compilePath,
+                            moduleCompilePath != null ? moduleCompilePath : ClassPath.EMPTY,
+                            moduleClassPath != null ? moduleClassPath : ClassPath.EMPTY,
+                            sourcePath,
+                            moduleSourcePath,
+                            virtualSourceTuples);
                 } finally {
                     JavaIndex.setAttribute(context.getRootURI(), ClassIndexManager.PROP_DIRTY_ROOT, Boolean.TRUE.toString());
                 }
@@ -222,6 +232,7 @@ protected void index(final Iterable<? extends Indexable> files, final Context co
                     javaContext.getClassIndexImpl().setDirty(null);
                     final SourceFileManager.ModifiedFilesTransaction mftx = txCtx.get(SourceFileManager.ModifiedFilesTransaction.class);
                     final boolean[] isModuleInfo = new boolean[1];
+                    URL[] binaries = null;
                     for (Indexable i : javaSources) {
                         final CompileTuple tuple = createTuple(context, javaContext, i);
                         if (tuple != null) {
@@ -241,6 +252,10 @@ protected void index(final Iterable<? extends Indexable> files, final Context co
                                     null :
                                     ElementHandleAccessor.getInstance().create(ElementKind.MODULE, moduleName);
                             JavaIndex.setAttribute(context.getRootURI(), JavaIndex.ATTR_MODULE_NAME, null);
+                            binaries = findBinaries(context.getRootURI());
+                            for (URL binary : binaries) {
+                                ModuleNames.getInstance().reset(binary);
+                            }
                         }
                     }
                     for (CompileTuple tuple : virtualSourceTuples) {
@@ -285,6 +300,12 @@ protected void index(final Iterable<? extends Indexable> files, final Context co
                     }
                     if (moduleName != null) {
                         JavaIndex.setAttribute(context.getRootURI(), JavaIndex.ATTR_MODULE_NAME, moduleName);
+                        if (binaries == null) {
+                            binaries = findBinaries(context.getRootURI());
+                        }
+                        for (URL binary : binaries) {
+                            ModuleNames.getInstance().reset(binary);
+                        }
                     }
                     finished = compileResult.success;
 
@@ -377,6 +398,15 @@ protected void index(final Iterable<? extends Indexable> files, final Context co
         }
     }
 
+    @NonNull
+    private static URL[] findBinaries(@NonNull final URL sourceRoot) throws IOException {
+        final URL[] artefacts = BinaryForSourceQuery.findBinaryRoots(sourceRoot).getRoots();
+        final List<URL> bin = new ArrayList<>(artefacts.length+1);
+        Collections.addAll(bin, artefacts);
+        bin.add(BaseUtilities.toURI(JavaIndex.getClassFolder(sourceRoot, false, false)).toURL());
+        return bin.toArray(new URL[bin.size()]);
+    }
+
     private static List<? extends Indexable> splitSources(final Iterable<? extends Indexable> indexables, final List<? super Indexable> javaSources) {
         List<Indexable> virtualSources = new LinkedList<Indexable>();
         for (Indexable indexable : indexables) {
diff --git a/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaParsingContext.java b/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaParsingContext.java
index e56cfc154..e6390b364 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaParsingContext.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/indexing/JavaParsingContext.java
@@ -129,6 +129,7 @@
                 context.isSourceForBinaryRootIndexing(),
                 false,
                 context.checkForEditorModifications(),
+                false,
                 null);
         } else {
             cpInfo = null;
@@ -140,9 +141,21 @@ public JavaParsingContext(final Context context, final ClassPath bootPath, final
         ctx = context;
         rootNotNeeded = false;
         uq = ClassIndexManager.getDefault().createUsagesQuery(context.getRootURI(), true);
-        cpInfo = ClasspathInfoAccessor.getINSTANCE().create(bootPath, moduleBootPath, compilePath, moduleCompilePath, moduleClassPath, sourcePath, moduleSourcePath,
-                filter, true, context.isSourceForBinaryRootIndexing(),
-                !virtualSources.isEmpty(), context.checkForEditorModifications(), null);
+        cpInfo = ClasspathInfoAccessor.getINSTANCE().create(
+                bootPath,
+                moduleBootPath,
+                compilePath,
+                moduleCompilePath,
+                moduleClassPath,
+                sourcePath,
+                moduleSourcePath,
+                filter,
+                true,
+                context.isSourceForBinaryRootIndexing(),
+                !virtualSources.isEmpty(),
+                context.checkForEditorModifications(),
+                true,
+                null);
         registerVirtualSources(cpInfo, virtualSources);
     }
     
diff --git a/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java b/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java
index 2791df72c..f97a11cd6 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java
@@ -56,6 +56,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -94,7 +95,6 @@
 import org.netbeans.api.java.source.JavaParserResultTask;
 import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.java.source.JavaSource.Phase;
-import org.netbeans.api.java.source.SourceUtils;
 import org.netbeans.api.project.FileOwnerQuery;
 import org.netbeans.api.project.Project;
 import org.netbeans.lib.editor.util.swing.PositionRegion;
@@ -366,10 +366,13 @@ private boolean shouldParse(@NonNull Task task) {
         }
         if (newSource.equals(oldSource)) {
             return false;
-        } else {
+        }
+        if (newSource.getClasspathInfo() == oldSource.getClasspathInfo()) {
             currentSource = new WeakReference<>(newSource);
-            return true;
+            return false;
         }
+        currentSource = new WeakReference<>(newSource);
+        return true;
     }
 
     private void parseImpl(
@@ -722,9 +725,6 @@ static JavacTaskImpl createJavacTask(
                             .filter((modName) -> modName != null)
                             .forEach(additionalModules::add);
                 }
-                if (additionalModules.isEmpty()) {
-                    additionalModules.add("ALL-MODULE-PATH");
-                }
             }
             final FileObject artefact = root != null ?
                     root :
@@ -841,7 +841,7 @@ private static JavacTaskImpl createJavacTask(
         boolean aptEnabled = aptUtils != null &&
                 aptUtils.aptEnabledOnScan() &&
                 (backgroundCompilation || (aptUtils.aptEnabledInEditor() && !multiSource)) &&
-                !ClasspathInfoAccessor.getINSTANCE().getCachedClassPath(cpInfo, PathKind.SOURCE).entries().isEmpty();
+                hasSourceCache(cpInfo, aptUtils);
         Collection<? extends Processor> processors = null;
         if (aptEnabled) {
             processors = aptUtils.resolveProcessors(backgroundCompilation);
@@ -1031,6 +1031,14 @@ private static JavacTaskImpl createJavacTask(
         for (int i = 0; i < options.size(); i++) {
             String option = options.get(i);
             if (option.startsWith("-Xmodule:") && !xmoduleSeen) {   //NOI18N
+                LOGGER.log(
+                        Level.WARNING,
+                        "Removed javac option -Xmodule: {0}",   //NOI18N
+                        option);
+                //Compatibility handle -Xmodule
+                res.add("-XD"+option);  //NOI18N
+                xmoduleSeen = true;
+            } else if (option.startsWith("-XD-Xmodule:") && !xmoduleSeen) { //NOI18N
                 res.add(option);
                 xmoduleSeen = true;
             } else if (option.equals("-parameters") || option.startsWith("-Xlint")) {     //NOI18N
@@ -1071,6 +1079,22 @@ private static boolean hasResource(
         return true;
     }
 
+    private static boolean hasSourceCache(
+            @NonNull final ClasspathInfo cpInfo,
+            @NonNull final APTUtils aptUtils) {
+        final List<? extends ClassPath.Entry> entries = ClasspathInfoAccessor.getINSTANCE().getCachedClassPath(cpInfo, PathKind.SOURCE).entries();
+        if (entries.isEmpty()) {
+            return false;
+        }
+        final URL sourceRoot = aptUtils.getRoot().toURL();
+        for (ClassPath.Entry entry : entries) {
+            if (sourceRoot.equals(entry.getURL())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
      private static boolean hasResource(
              @NonNull final String resource,
              @NonNull final ClassPath... cps) {
@@ -1348,7 +1372,10 @@ public boolean isCanceled() {
         }
     }
 
-    private void checkSourceModification(final SourceModificationEvent evt) {
+    private void checkSourceModification(SourceModificationEvent evt) {
+        if (evt instanceof SourceModificationEvent.Composite) {
+            evt = ((SourceModificationEvent.Composite)evt).getWriteEvent();
+        }
         if (evt != null && evt.sourceChanged()) {
             Pair<DocPositionRegion,MethodTree> changedMethod = null;
             if (supportsReparse) {
diff --git a/java.source.base/src/org/netbeans/modules/java/source/parsing/ModuleOraculum.java b/java.source.base/src/org/netbeans/modules/java/source/parsing/ModuleOraculum.java
index 45cd52aec..ac4ed42b8 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/parsing/ModuleOraculum.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/parsing/ModuleOraculum.java
@@ -253,7 +253,7 @@ private void resetModNameCache() {
         R(@NonNull final String moduleName) {
             Parameters.notNull("moduleName", moduleName);   //NOI18N
             this.ops = Collections.singletonList(String.format(
-                    "-Xmodule:%s",  //NOI18N
+                    "-XD-Xmodule:%s",  //NOI18N
                     moduleName));
         }
 
diff --git a/java.source.base/src/org/netbeans/modules/java/source/parsing/OutputFileManager.java b/java.source.base/src/org/netbeans/modules/java/source/parsing/OutputFileManager.java
index 0bd03004b..f78c7aaa9 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/parsing/OutputFileManager.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/parsing/OutputFileManager.java
@@ -61,7 +61,7 @@
     /**
      * Exception used to signal that the sourcepath is broken (project is deleted)
      */
-    public class InvalidSourcePath extends IllegalStateException {
+    public static class InvalidSourcePath extends IllegalStateException {
     }
 
     private final ClassPath scp;
diff --git a/java.source.base/src/org/netbeans/modules/java/source/usages/BinaryAnalyser.java b/java.source.base/src/org/netbeans/modules/java/source/usages/BinaryAnalyser.java
index ec255773f..ed378b3ea 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/usages/BinaryAnalyser.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/usages/BinaryAnalyser.java
@@ -693,30 +693,46 @@ private static int getSimpleNameIndex(@NonNull final ClassFile cf) {
             return 0;
         }
         final ClassName me = cf.getName();
-        final String simpleName = me.getSimpleName();
+        final String simpleName = getInternalSimpleName(me);
         int len = simpleName.length();
         for (InnerClass ic : cf.getInnerClasses()) {
             if (me.equals(ic.getName())) {
                 final String innerName = ic.getSimpleName();
+                boolean found = false;
                 if (innerName != null && !innerName.isEmpty()) {
-                    len = innerName.length();
+                    if (simpleName.endsWith(innerName)) {
+                        len = innerName.length();
+                        found = true;
+                    }
                 } else {
                     final EnclosingMethod enclosingMethod = cf.getEnclosingMethod();
                     if (enclosingMethod != null) {
-                        final String encSimpleName = enclosingMethod.getClassName().getSimpleName();
-                        len -= encSimpleName.length() + 1;
-                    } else {
-                        final int sepIndex = simpleName.lastIndexOf('.');   //NOI18N
-                        if (sepIndex > 0) {
-                            len -= sepIndex+1;
+                        final String encSimpleName = getInternalSimpleName(enclosingMethod.getClassName());
+                        if (simpleName.startsWith(encSimpleName)) {
+                            len -= encSimpleName.length() + 1;
+                            found = true;
                         }
                     }
                 }
+                if (!found) {
+                    final int sepIndex = simpleName.lastIndexOf('$');   //NOI18N
+                    if (sepIndex > 0) {
+                        len -= sepIndex+1;
+                    }
+                }
                 break;
             }
         }
         return me.getInternalName().length() - len;
     }
+
+    private static String getInternalSimpleName(@NonNull final ClassName name) {
+        final String pkg = name.getPackage();
+        final String internalName = name.getInternalName();
+        return pkg.isEmpty() ?
+                internalName :
+                internalName.substring(pkg.length()+1);
+    }
     //</editor-fold>
 
     //<editor-fold defaultstate="collapsed" desc="ClassFileProcessor implementations">
diff --git a/java.source.base/src/org/netbeans/modules/java/source/usages/ClasspathInfoAccessor.java b/java.source.base/src/org/netbeans/modules/java/source/usages/ClasspathInfoAccessor.java
index acaab07c1..91a593589 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/usages/ClasspathInfoAccessor.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/usages/ClasspathInfoAccessor.java
@@ -79,6 +79,7 @@ public abstract ClasspathInfo create (
             boolean ignoreExcludes,
             boolean hasMemoryFileManager,
             boolean useModifiedFiles,
+            boolean requiresSourceRoots,
             @NullAllowed Function<JavaFileManager.Location, JavaFileManager> jfmProvider);
 
     public abstract ClasspathInfo create (FileObject fo,
diff --git a/java.source.base/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java b/java.source.base/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java
index f72d9f71e..68344b747 100644
--- a/java.source.base/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java
+++ b/java.source.base/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java
@@ -72,6 +72,7 @@
     //@GuardedBy("this")
     private Set<String> rootPkgCache;
     private volatile FileObject cachedRoot;
+    @SuppressWarnings("VolatileArrayField")
     private volatile FileObject[] cachedAptRoots;
     private static final Logger LOGGER = Logger.getLogger(PersistentClassIndex.class.getName());
     private static final String REFERENCES = "refs";    // NOI18N
@@ -140,6 +141,7 @@ public boolean isValid() {
 
     @Override
     @NonNull
+    @SuppressWarnings("NestedAssignment")
     public FileObject[] getSourceRoots () {
         if (getType() == Type.SOURCE) {
             final FileObject rootFo = getRoot();
@@ -182,7 +184,7 @@ public boolean isValid() {
     public String getSourceName (final String binaryName) throws IOException, InterruptedException {
         try {
             final Query q = DocumentUtil.binaryNameQuery(binaryName);
-            Set<String> names = new HashSet<String>();
+            Set<String> names = new HashSet<>();
             index.query(names, DocumentUtil.sourceNameConvertor(), DocumentUtil.sourceNameFieldSelector(), cancel.get(), q);
             return names.isEmpty() ? null : names.iterator().next();
         } catch (IOException e) {
@@ -220,20 +222,17 @@ public static ClassIndexImpl create(
             final String binaryName = SourceUtils.getJVMSignature(element)[0];
             final ElementKind kind = element.getKind();
             if (kind == ElementKind.PACKAGE) {
-                IndexManager.priorityAccess(new IndexManager.Action<Void> () {
-                    @Override
-                    public Void run () throws IOException, InterruptedException {
-                        final Query q = QueryUtil.scopeFilter(
-                                QueryUtil.createPackageUsagesQuery(binaryName,usageType,Occur.SHOULD),
-                                scope);
-                        if (q!=null) {
-                            index.query(result, ctu.first(), DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), q);
-                            if (ctu.second() != null) {
-                                ctu.second().query(result, convertor, DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), q);
-                            }
+                IndexManager.priorityAccess(() -> {
+                    final Query q = QueryUtil.scopeFilter(
+                            QueryUtil.createPackageUsagesQuery(binaryName,usageType,Occur.SHOULD),
+                            scope);
+                    if (q != null) {
+                        index.query(result, ctu.first(), DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), q);
+                        if (ctu.second() != null) {
+                            ctu.second().query(result, convertor, DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), q);
                         }
-                        return null;
                     }
+                    return null;
                 });
             } else if (kind.isClass() ||
                        kind.isInterface() ||
@@ -247,20 +246,17 @@ public Void run () throws IOException, InterruptedException {
                         convertor,
                         result);
                 } else {
-                    IndexManager.priorityAccess(new IndexManager.Action<Void> () {
-                        @Override
-                        public Void run () throws IOException, InterruptedException {
-                            final Query usagesQuery = QueryUtil.scopeFilter(
-                                    QueryUtil.createUsagesQuery(binaryName, usageType, Occur.SHOULD),
-                                    scope);
-                            if (usagesQuery != null) {
-                                index.query(result, ctu.first(), DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), usagesQuery);
-                                if (ctu.second() != null) {
-                                    ctu.second().query(result, convertor, DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), usagesQuery);
-                                }
+                    IndexManager.priorityAccess(() -> {
+                        final Query usagesQuery = QueryUtil.scopeFilter(
+                                QueryUtil.createUsagesQuery(binaryName, usageType, Occur.SHOULD),
+                                scope);
+                        if (usagesQuery != null) {
+                            index.query(result, ctu.first(), DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), usagesQuery);
+                            if (ctu.second() != null) {
+                                ctu.second().query(result, convertor, DocumentUtil.declaredTypesFieldSelector(false, false), cancel.get(), usagesQuery);
                             }
-                            return null;
                         }
+                        return null;
                     });
                 }
             } else {
@@ -282,24 +278,21 @@ public Void run () throws IOException, InterruptedException {
             @NonNull final Collection<? super T> result) throws InterruptedException, IOException {
         final Pair<Convertor<? super Document, T>,Index> ctu = indexPath.getPatch(convertor);
         try {
-            IndexManager.priorityAccess(new IndexManager.Action<Void> () {
-                @Override
-                public Void run () throws IOException, InterruptedException {
-                    final Query query =  QueryUtil.scopeFilter(
+            IndexManager.priorityAccess(() -> {
+                final Query query =  QueryUtil.scopeFilter(
                         Queries.createQuery(
-                            DocumentUtil.FIELD_SIMPLE_NAME,
-                            DocumentUtil.FIELD_CASE_INSENSITIVE_NAME,
-                            simpleName,
-                            DocumentUtil.translateQueryKind(kind)),
+                                DocumentUtil.FIELD_SIMPLE_NAME,
+                                DocumentUtil.FIELD_CASE_INSENSITIVE_NAME,
+                                simpleName,
+                                DocumentUtil.translateQueryKind(kind)),
                         scope);
-                    if (query != null) {
-                        index.query(result, ctu.first(), selector, cancel.get(), query);
-                        if (ctu.second() != null) {
-                            ctu.second().query(result, convertor, selector, cancel.get(), query);
-                        }
+                if (query != null) {
+                    index.query(result, ctu.first(), selector, cancel.get(), query);
+                    if (ctu.second() != null) {
+                        ctu.second().query(result, convertor, selector, cancel.get(), query);
                     }
-                    return null;
                 }
+                return null;
             });
         } catch (IOException ioe) {
             this.<Void,IOException>handleException(null, ioe, root);
@@ -314,39 +307,29 @@ public Void run () throws IOException, InterruptedException {
             final Map<T,Set<String>> result) throws InterruptedException, IOException {
         final Pair<Convertor<? super Document, T>,Index> ctu = indexPath.getPatch(convertor);
         try {
-            IndexManager.priorityAccess(new IndexManager.Action<Void>() {
-                @Override
-                public Void run () throws IOException, InterruptedException {
-                    final Query query = Queries.createTermCollectingQuery(
-                            DocumentUtil.FIELD_FEATURE_IDENTS,
-                            DocumentUtil.FIELD_CASE_INSENSITIVE_FEATURE_IDENTS,
-                            ident,
-                            DocumentUtil.translateQueryKind(kind));
-                    final Convertor<Term, String> t2s =
-                        new Convertor<Term, String>(){
-                            @Override
-                            public String convert(Term p) {
-                                return p.text();
-                            }
-                        };
-                    index.queryDocTerms(
-                            result,
-                            ctu.first(),
-                            t2s,
-                            DocumentUtil.declaredTypesFieldSelector(false, false),
-                            cancel.get(),
-                            query);
-                    if (ctu.second() != null) {
-                        ctu.second().queryDocTerms(
+            IndexManager.priorityAccess(() -> {
+                final Query query = Queries.createTermCollectingQuery(
+                        DocumentUtil.FIELD_FEATURE_IDENTS,
+                        DocumentUtil.FIELD_CASE_INSENSITIVE_FEATURE_IDENTS,
+                        ident,
+                        DocumentUtil.translateQueryKind(kind));
+                index.queryDocTerms(
+                        result,
+                        ctu.first(),
+                        Term::text,
+                        DocumentUtil.declaredTypesFieldSelector(false, false),
+                        cancel.get(),
+                        query);
+                if (ctu.second() != null) {
+                    ctu.second().queryDocTerms(
                             result,
                             convertor,
-                            t2s,
+                            Term::text,
                             DocumentUtil.declaredTypesFieldSelector(false, false),
                             cancel.get(),
                             query);
-                    }
-                    return null;
                 }
+                return null;
             });
         } catch (IOException ioe) {
             this.<Void,IOException>handleException(null, ioe, root);
@@ -357,36 +340,33 @@ public String convert(Term p) {
     @Override
     public void getPackageNames (final String prefix, final boolean directOnly, final Set<String> result) throws InterruptedException, IOException {
         try {
-            IndexManager.priorityAccess(new IndexManager.Action<Void>() {
-                @Override
-                public Void run () throws IOException, InterruptedException {
-                    final boolean cacheOp = directOnly && prefix.length() == 0;
-                    Set<String> myPkgs = null;
-                    Collection<String> collectInto;
-                    if (cacheOp) {
-                        synchronized (PersistentClassIndex.this) {
-                            if (rootPkgCache != null) {
-                                result.addAll(rootPkgCache);
-                                return null;
-                            }
+            IndexManager.priorityAccess(() -> {
+                final boolean cacheOp = directOnly && prefix.length() == 0;
+                Set<String> myPkgs = null;
+                Collection<String> collectInto;
+                if (cacheOp) {
+                    synchronized (PersistentClassIndex.this) {
+                        if (rootPkgCache != null) {
+                            result.addAll(rootPkgCache);
+                            return null;
                         }
-                        myPkgs = new HashSet<String>();
-                        collectInto = new TeeCollection(myPkgs,result);
-                    } else {
-                        collectInto = result;
                     }
-                    final Pair<StoppableConvertor<Term,String>,Term> filter = QueryUtil.createPackageFilter(prefix,directOnly);
-                    index.queryTerms(collectInto, filter.second(), filter.first(), cancel.get());
-                    if (cacheOp) {
-                        synchronized (PersistentClassIndex.this) {
-                            if (rootPkgCache == null) {
-                                assert myPkgs != null;
-                                rootPkgCache = myPkgs;
-                            }
+                    myPkgs = new HashSet<>();
+                    collectInto = new TeeCollection(myPkgs,result);
+                } else {
+                    collectInto = result;
+                }
+                final Pair<StoppableConvertor<Term,String>,Term> filter = QueryUtil.createPackageFilter(prefix,directOnly);
+                index.queryTerms(collectInto, filter.second(), filter.first(), cancel.get());
+                if (cacheOp) {
+                    synchronized (PersistentClassIndex.this) {
+                        if (rootPkgCache == null) {
+                            assert myPkgs != null;
+                            rootPkgCache = myPkgs;
                         }
                     }
-                    return null;
                 }
+                return null;
             });
         } catch (IOException ioe) {
             this.<Void,IOException>handleException(null, ioe, root);
@@ -403,19 +383,16 @@ public void getReferencesFrequences (
             throw new IllegalStateException("Index does not support frequencies!"); //NOI18N
         }
         try {
-            IndexManager.priorityAccess(new IndexManager.Action<Void>() {
-                @Override
-                public Void run() throws IOException, InterruptedException {
-                    final Term startTerm = DocumentUtil.referencesTerm("", null, true);    //NOI18N
-                    final StoppableConvertor<Index.WithTermFrequencies.TermFreq,Void> convertor = new FreqCollector(
-                            startTerm, typeFreq, pkgFreq);
-                    ((Index.WithTermFrequencies)index).queryTermFrequencies(
+            IndexManager.priorityAccess(() -> {
+                final Term startTerm = DocumentUtil.referencesTerm("", null, true);    //NOI18N
+                final StoppableConvertor<Index.WithTermFrequencies.TermFreq,Void> convertor = new FreqCollector(
+                        startTerm, typeFreq, pkgFreq);
+                ((Index.WithTermFrequencies)index).queryTermFrequencies(
                         Collections.<Void>emptyList(),
                         startTerm,
                         convertor,
                         cancel.get());
-                    return null;
-                }
+                return null;
             });
         } catch (IOException ioe) {
             this.<Void,IOException>handleException(null, ioe, root);
@@ -507,8 +484,8 @@ public void deleteAndStore(List<Pair<Pair<BinaryName,String>, Object[]>> refs, S
 
     private static class TeeCollection<T> extends AbstractCollection<T> {
 
-        private Collection<T> primary;
-        private Collection<T> secondary;
+        private final Collection<T> primary;
+        private final Collection<T> secondary;
 
 
         TeeCollection(final @NonNull Collection<T> primary, @NonNull Collection<T> secondary) {
@@ -553,7 +530,7 @@ public boolean add(T e) {
                 final Pair<Index,Set<String>> data = updateDirty();
                 if (data != null) {
                     return Pair.<Convertor<? super Document, T>,Index>of(
-                            new FilterConvertor<T>(data.second(), delegate),
+                            new FilterConvertor<>(data.second(), delegate),
                             data.first());
                 }
             } catch (IOException ioe) {
@@ -581,38 +558,33 @@ synchronized void setDirtyFile(@NullAllowed final URL url) throws IOException {
                 result = indexPatch;
             }
             if (url != null) {
-                final FileObject file = url != null ? URLMapper.findFileObject(url) : null;
+                final FileObject file = URLMapper.findFileObject(url);
                 final JavaSource js = file != null ? JavaSource.forFileObject(file) : null;
                 final long startTime = System.currentTimeMillis();
                 final List[] dataHolder = new List[1];
                 if (js != null) {
                     final ClassPath scp = js.getClasspathInfo().getClassPath(PathKind.SOURCE);
                     if (scp != null && scp.contains(file)) {
-                        js.runUserActionTask(new Task<CompilationController>() {
-                            @Override
-                            public void run (final CompilationController controller) {
+                        js.runUserActionTask((final CompilationController controller) -> {
+                            try {
+                                if (controller.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED)<0) {
+                                    return;
+                                }
                                 try {
-                                    if (controller.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED)<0) {
-                                        return;
-                                    }
-                                    try {
-                                        final SourceAnalyzerFactory.SimpleAnalyzer sa = SourceAnalyzerFactory.createSimpleAnalyzer();
-                                        dataHolder[0] = sa.analyseUnit(
+                                    final SourceAnalyzerFactory.SimpleAnalyzer sa = SourceAnalyzerFactory.createSimpleAnalyzer();
+                                    dataHolder[0] = sa.analyseUnit(
                                             controller.getCompilationUnit(),
                                             JavaSourceAccessor.getINSTANCE().getJavacTask(controller));
-                                    } catch (IllegalArgumentException ia) {
-                                        //Debug info for issue #187344
-                                        //seems that invalid dirty class index is used
-                                        final ClassPath scp = controller.getClasspathInfo().getClassPath(PathKind.SOURCE);
-                                        throw new IllegalArgumentException(
-                                                String.format("Provided source path: %s root: %s",    //NOI18N
-                                                    scp == null ? "<null>" : scp.toString(),    //NOI18N
-                                                    root.toExternalForm()),
-                                                ia);
-                                    }
-                                } catch (IOException ioe) {
-                                    Exceptions.printStackTrace(ioe);
+                                } catch (IllegalArgumentException ia) {
+                                    //Debug info for issue #187344
+                                    //seems that invalid dirty class index is used
+                                    final ClassPath scp1 = controller.getClasspathInfo().getClassPath(PathKind.SOURCE);
+                                    throw new IllegalArgumentException(String.format("Provided source path: %s root: %s", //NOI18N
+                                    scp1 == null ? "<null>" : scp1.toString(), //NOI18N
+                                    root.toExternalForm()), ia);
                                 }
+                            }catch (IOException ioe) {
+                                Exceptions.printStackTrace(ioe);
                             }
                         }, true);
                     } else {
@@ -630,28 +602,38 @@ public void run (final CompilationController controller) {
                 if (data != null) {
                     if (filter == null) {
                         try {
-                            filter = new HashSet<String>();
+                            filter = new HashSet<>();
                             assert file != null : "Null file for URL: " + url;  //NOI18N
                             final FileObject root = getRoot();
-                            final String relPath = FileUtil.getRelativePath(root, file);
-                            if (relPath != null) {
-                                final String clsName = FileObjects.convertFolder2Package(
-                                        FileObjects.stripExtension(relPath));
-                                index.query(
-                                        filter,
-                                        DocumentUtil.binaryNameConvertor(),
-                                        DocumentUtil.declaredTypesFieldSelector(false, false),
-                                        null,
-                                        DocumentUtil.queryClassWithEncConvertor(true).convert(Pair.<String,String>of(clsName,relPath)));
+                            if (root != null) {
+                                final String relPath = FileUtil.getRelativePath(root, file);
+                                if (relPath != null) {
+                                        final String clsName = FileObjects.convertFolder2Package(
+                                                FileObjects.stripExtension(relPath));
+                                        index.query(
+                                                filter,
+                                                DocumentUtil.binaryNameConvertor(),
+                                                DocumentUtil.declaredTypesFieldSelector(false, false),
+                                                null,
+                                                DocumentUtil.queryClassWithEncConvertor(true).convert(Pair.<String,String>of(clsName,relPath)));
+                                } else {
+                                    LOGGER.log(
+                                        Level.WARNING,
+                                        "File: {0}({1}) is not owned by root: {2}({3})",    //NOI18N
+                                        new Object[]{
+                                            FileUtil.getFileDisplayName(file),
+                                            file.isValid(),
+                                            FileUtil.getFileDisplayName(root),
+                                            root.isValid()
+                                        });
+                                }
                             } else {
                                 LOGGER.log(
                                     Level.WARNING,
-                                    "File: {0}({1}) is not owned by root: {2}({3})",
+                                    "File: {0}({1}) is has no root.",   //NOI18N
                                     new Object[]{
                                         FileUtil.getFileDisplayName(file),
-                                        file.isValid(),
-                                        FileUtil.getFileDisplayName(root),
-                                        root.isValid()
+                                        file.isValid()
                                     });
                             }
                         } catch (InterruptedException ie) {
@@ -738,6 +720,7 @@ public T convert(F p) {
 
         @CheckForNull
         @Override
+        @SuppressWarnings("StringEquality")
         public Void convert(@NonNull final Index.WithTermFrequencies.TermFreq param) throws Stop {
             final Term term = param.getTerm();
             if (fieldName != term.field()) {
@@ -759,6 +742,7 @@ public Void convert(@NonNull final Index.WithTermFrequencies.TermFreq param) thr
     }
 
     @CheckForNull
+    @SuppressWarnings("NestedAssignment")
     private FileObject getRoot() {
         FileObject res = cachedRoot;
         if (res == null || !res.isValid()) {
diff --git a/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ImportAnalysis2Test.java b/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ImportAnalysis2Test.java
index d56c367da..35ea47fdd 100644
--- a/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ImportAnalysis2Test.java
+++ b/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ImportAnalysis2Test.java
@@ -623,7 +623,7 @@ public void test195882() throws Exception {
             "    }\n" +
             "}\n";
 
-        ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, null);
+        ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, false, null);
         JavaSource src = JavaSource.create(cpInfo, FileUtil.toFileObject(testFile));
         Task<WorkingCopy> task = new Task<WorkingCopy>() {
             public void run(WorkingCopy workingCopy) throws IOException {
@@ -667,7 +667,7 @@ public void testParameterizedType() throws Exception {
             "    Entry e;\n" +
             "}\n";
 
-        ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, null);
+        ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, false, null);
         JavaSource src = JavaSource.create(cpInfo, FileUtil.toFileObject(testFile));
         Task<WorkingCopy> task = new Task<WorkingCopy>() {
             public void run(WorkingCopy workingCopy) throws IOException {
@@ -708,7 +708,7 @@ public void testTooSoon206957a() throws Exception {
             "}\n";
         final TransactionContext ctx = TransactionContext.beginStandardTransaction(Utilities.toURI(getWorkDir()).toURL(), true, true, false);
         try {
-            ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, null);
+            ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, false, null);
             JavaSource src = JavaSource.create(cpInfo, FileUtil.toFileObject(testFile));
             Preferences preferences = MimeLookup.getLookup(JavaTokenId.language().mimeType()).lookup(Preferences.class);
             preferences.putBoolean("importInnerClasses", true);
@@ -754,7 +754,7 @@ public void testTooSoon206957b() throws Exception {
 
         final TransactionContext ctx = TransactionContext.beginStandardTransaction(Utilities.toURI(getWorkDir()).toURL(), true, true, false);
         try {
-            ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, null);
+            ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create (ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")), ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY, ClassPathSupport.createClassPath(getSourcePath()), ClassPath.EMPTY, null, true, false, false, true, false, null);
             JavaSource src = JavaSource.create(cpInfo, FileUtil.toFileObject(testFile));
             Task<WorkingCopy> task = new Task<WorkingCopy>() {
                 public void run(WorkingCopy workingCopy) throws IOException {
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/ModuleNamesTest.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/ModuleNamesTest.java
new file mode 100644
index 000000000..9fc2cc826
--- /dev/null
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/ModuleNamesTest.java
@@ -0,0 +1,1184 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.ClientCodeWrapper;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import java.util.zip.ZipEntry;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.swing.event.ChangeListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import org.netbeans.ProxyURLStreamHandlerFactory;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.platform.JavaPlatform;
+import org.netbeans.api.java.queries.BinaryForSourceQuery;
+import org.netbeans.api.java.queries.SourceForBinaryQuery;
+import org.netbeans.junit.MockServices;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.java.source.indexing.JavaIndex;
+import org.netbeans.modules.java.source.parsing.Archive;
+import org.netbeans.modules.java.source.parsing.CachingArchiveProvider;
+import org.netbeans.modules.java.source.parsing.FileObjects;
+import org.netbeans.modules.java.source.parsing.InferableJavaFileObject;
+import org.netbeans.modules.java.source.usages.ClassIndexEventsTransaction;
+import org.netbeans.modules.java.source.usages.ClassIndexImpl;
+import org.netbeans.modules.java.source.usages.ClassIndexManager;
+import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
+import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2;
+import org.openide.filesystems.FileLock;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.ChangeSupport;
+import org.openide.util.Lookup;
+import org.openide.util.Pair;
+
+/**
+ * Test for {@link ModuleNames}.
+ * @author Tomas Zezula
+ */
+public class ModuleNamesTest extends NbTestCase {
+    private static final String RES_MANIFEST = "META-INF/MANIFEST.MF";  //NOI18N
+    private FileObject wd;
+    private ModuleNames names;
+
+    public ModuleNamesTest(@NonNull final String name) {
+        super(name);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        clearWorkDir();
+        ProxyURLStreamHandlerFactory.register();
+        MockServices.setServices(BinAndSrc.class, NBJRTStreamHandlerFactory.class, AutomaticModuleName.class);
+        wd = FileUtil.toFileObject(getWorkDir());
+        assertNotNull(wd);
+        final FileObject cache = FileUtil.createFolder(wd, "cache");    //NOI18N
+        CacheFolder.setCacheFolder(cache);
+        names = ModuleNames.getInstance();
+        assertNotNull(names);
+    }
+
+    public void testAutomaticModule() throws Exception {
+        final TraceHandler th = TraceHandler.register();
+        try {
+            FileObject mod1 = FileUtil.getArchiveRoot(jar(wd, "app-core-1.0.jar", null).get());    //NOI18N
+            final FileObject mod2 = FileUtil.getArchiveRoot(jar(wd,"app-main-1.0.jar", null).get());     //NOI18N
+            String moduleName = names.getModuleName(mod1.toURL(), false);
+            assertEquals("app.core", moduleName);   //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod2.toURL(), false);
+            assertEquals("app.main", moduleName);   //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod1.toURL(), false);
+            assertEquals("app.core", moduleName);   //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod2.toURL(), false);
+            assertEquals("app.main", moduleName);   //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            final URL origURL = mod2.toURL();
+            final FileObject mod2new = rename(mod2, "app-ui-1.0.jar");    //NOI18N
+            moduleName = names.getModuleName(mod2new.toURL(), false);
+            assertEquals("app.ui", moduleName);   //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(origURL, false);
+            assertNull(moduleName);
+            assertTrue(th.isCalculated());
+            th.reset();
+            mod1.getFileSystem().removeNotify();    //Close JAR before changing it, otherwise JarFS may be created with wrong content.
+            FileUtil.getArchiveFile(mod1).delete();
+            FileObject jar = jar(wd,
+                    "app-core-1.0.jar", //NOI18N
+                    () -> Collections.singleton(Pair.of(
+                            "module-info.class",    //NOI18N
+                            moduleInfoClz(moduleInfoJava("org.me.app.core", Collections.emptyList())).get()))   //NOI18N
+                    ).get();
+            mod1 = FileUtil.getArchiveRoot(jar);
+            moduleName = names.getModuleName(mod1.toURL(), false);
+            assertTrue(th.isCalculated());
+            assertEquals("org.me.app.core", moduleName);    //NOI18N
+            th.reset();
+        } finally {
+            th.unregister();
+        }
+    }
+
+    public void testAutomaticModuleWithManifestAttribute() throws Exception {
+        final TraceHandler th = TraceHandler.register();
+        try {
+            FileObject mod = FileUtil.getArchiveRoot(jar(
+                    wd,
+                    "app-core-1.0.jar", //NOI18N
+                    () -> {
+                        try {
+                            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+                            final Manifest mf = new Manifest();
+                            mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");    //NOI18N
+                            mf.getMainAttributes().putValue("Automatic-Module-Name", "org.me.app.core");    //NOI18N
+                            mf.write(out);
+                            return Collections.singleton(Pair.of(
+                                    RES_MANIFEST,
+                                    out.toByteArray()
+                            ));
+                        } catch (IOException ioe) {
+                            throw new RuntimeException(ioe);
+                        }
+                    }
+            ).get());
+            String moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("org.me.app.core", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("org.me.app.core", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            mod.getFileSystem().removeNotify();    //Close JAR before changing it, otherwise JarFS may be created with wrong content.
+            mod = FileUtil.getArchiveRoot(jar(
+                    wd,
+                    "app-core-1.0.jar", //NOI18N
+                    null).get());
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("app.core", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("app.core", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            mod.getFileSystem().removeNotify();    //Close JAR before changing it, otherwise JarFS may be created with wrong content.
+            mod = FileUtil.getArchiveRoot(jar(
+                    wd,
+                    "app-core-1.0.jar", //NOI18N
+                    () -> {
+                        try {
+                            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+                            final Manifest mf = new Manifest();
+                            mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");    //NOI18N
+                            mf.getMainAttributes().putValue("Automatic-Module-Name", "org.me.app.core");    //NOI18N
+                            mf.write(out);
+                            return Collections.singleton(Pair.of(
+                                    RES_MANIFEST,
+                                    out.toByteArray()
+                            ));
+                        } catch (IOException ioe) {
+                            throw new RuntimeException(ioe);
+                        }
+                    }
+            ).get());    //NOI18N
+            mod = FileUtil.getArchiveRoot(jar(
+                    wd,
+                    "app-core-1.0.jar", //NOI18N
+                    () -> {
+                        try {
+                            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+                            final Manifest mf = new Manifest();
+                            mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");    //NOI18N
+                            mf.getMainAttributes().putValue("Automatic-Module-Name", "com.me.app.core");    //NOI18N
+                            mf.write(out);
+                            return Collections.singleton(Pair.of(
+                                    RES_MANIFEST,
+                                    out.toByteArray()
+                            ));
+                        } catch (IOException ioe) {
+                            throw new RuntimeException(ioe);
+                        }
+                    }
+            ).get());    //NOI18N
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("com.me.app.core", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("com.me.app.core", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+        } finally {
+            th.unregister();
+        }
+    }
+
+    public void testNamedModule() throws Exception {
+        final TraceHandler th = TraceHandler.register();
+        try {
+            FileObject mod = FileUtil.getArchiveRoot(jar(
+                    wd,
+                    "dist.jar", //NOI18N
+                    () -> Collections.singleton(Pair.of(
+                            "module-info.class",    //NOI18N
+                            moduleInfoClz(moduleInfoJava("org.me.app", Collections.emptyList())).get()))        //NOI18N
+                    ).get());
+            String moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("org.me.app", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("org.me.app", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            mod.getFileSystem().removeNotify();    //Close JAR before changing it, otherwise JarFS may be created with wrong content.
+            FileUtil.getArchiveFile(mod).delete();
+            FileObject jar = jar(wd,
+                    "dist.jar", //NOI18N
+                    () -> Collections.singleton(Pair.of(
+                            "module-info.class",    //NOI18N
+                            moduleInfoClz(moduleInfoJava("com.me.app", Collections.emptyList())).get()))    //NOI18N
+                    ).get();
+            mod = FileUtil.getArchiveRoot(jar);
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertTrue(th.isCalculated());
+            assertEquals("com.me.app", moduleName);    //NOI18N
+            th.reset();
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertEquals("com.me.app", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            mod.getFileSystem().removeNotify();    //Close JAR before changing it, otherwise JarFS may be created with wrong content.
+            FileUtil.getArchiveFile(mod).delete();
+            jar = jar(wd, "dist.jar", null).get();   //NOI18N
+            mod = FileUtil.getArchiveRoot(jar);
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertTrue(th.isCalculated());
+            assertEquals("dist", moduleName);    //NOI18N
+            th.reset();
+            moduleName = names.getModuleName(mod.toURL(), false);
+            assertFalse(th.isCalculated());
+            assertEquals("dist", moduleName);    //NOI18N
+        } finally {
+            th.unregister();
+        }
+    }
+
+    public void testProject() throws Exception {
+        final FileObject src = FileUtil.createFolder(wd, "src");    //NOI18N
+        final Supplier<Pair<Boolean, String>> mij = moduleInfoJava("org.me.prj", Collections.emptySet());   //NOI18N
+        writeFile(src, "module-info.java", () -> mij.get().second()).get();   //NOI18N
+        final FileObject dist = FileUtil.createFolder(wd, "dist");      //NOI18N
+        final File distJar = new File(FileUtil.toFile(dist), "prj.jar");    //NOI18N
+        final URL distJarURL = FileUtil.urlForArchiveOrDir(distJar);
+        BinAndSrc.getInstance().register(src.toURL(), distJarURL);
+        final SourceForBinaryQuery.Result2 sres = SourceForBinaryQuery.findSourceRoots2(distJarURL);
+        assertTrue(sres.preferSources());
+        assertEquals(Arrays.asList(src), Arrays.asList(sres.getRoots()));
+        final BinaryForSourceQuery.Result bres = BinaryForSourceQuery.findBinaryRoots(src.toURL());
+        assertEquals(Arrays.asList(distJarURL), Arrays.asList(bres.getRoots()));
+
+        final TraceHandler th = TraceHandler.register();
+        try {
+            String moduleName = names.getModuleName(distJarURL, false);
+            assertNull(moduleName);
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertNull(moduleName);
+            assertTrue(th.isCalculated());  //null not cached as it may differ for getModuleName(root, true);
+            th.reset();
+            fakeIndex(src, distJarURL, "org.me.prj");   //NOI18N
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.prj", moduleName);     //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.prj", moduleName);     //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            final Supplier<Pair<Boolean, String>> mij2 = moduleInfoJava("org.me.foo", Collections.emptySet());   //NOI18N
+            final FileObject modInfo = writeFile(src, "module-info.java", () -> mij2.get().second()).get();   //NOI18N
+            fakeIndex(src, distJarURL, "org.me.foo");   //NOI18N
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.foo", moduleName);     //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.foo", moduleName);     //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            modInfo.delete();
+            fakeIndex(src, distJarURL, null);
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("prj", moduleName);     //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("prj", moduleName);     //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+        } finally {
+            th.unregister();
+        }
+    }
+
+    public void testAutomaticProject() throws IOException {
+        final FileObject src = FileUtil.createFolder(wd, "src");    //NOI18N
+        final FileObject dist = FileUtil.createFolder(wd, "dist");      //NOI18N
+        final File distJar = new File(FileUtil.toFile(dist), "prj.jar");    //NOI18N
+        final URL distJarURL = FileUtil.urlForArchiveOrDir(distJar);
+        BinAndSrc.getInstance().register(src.toURL(), distJarURL);
+        final SourceForBinaryQuery.Result2 sres = SourceForBinaryQuery.findSourceRoots2(distJarURL);
+        assertTrue(sres.preferSources());
+        assertEquals(Arrays.asList(src), Arrays.asList(sres.getRoots()));
+        final BinaryForSourceQuery.Result bres = BinaryForSourceQuery.findBinaryRoots(src.toURL());
+        assertEquals(Arrays.asList(distJarURL), Arrays.asList(bres.getRoots()));
+        final TraceHandler th = TraceHandler.register();
+        AutomaticModuleName.getInstance().register(src, null);  //Register Result for root to listen on it.
+        try {
+            fakeIndex(src, distJarURL, null);
+            String moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("prj", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("prj", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            AutomaticModuleName.getInstance().register(src, "org.me.foo");  //NOI18N
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.foo", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.foo", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            AutomaticModuleName.getInstance().register(src, "org.me.boo");  //NOI18N
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.boo", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("org.me.boo", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            AutomaticModuleName.getInstance().register(src, null);
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("prj", moduleName);    //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(distJarURL, false);
+            assertEquals("prj", moduleName);    //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+        } finally {
+            th.unregister();
+        }
+    }
+
+    public void testPlatform() throws Exception {
+        final TraceHandler th = TraceHandler.register();
+        final URL JAVA_BASE = new URL("nbjrt:file:/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/!/modules/java.base/");   //NOI18N
+        final URL JVMCI = new URL("nbjrt:file:/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/!/modules/jdk.internal.vm.ci/"); //NOI18N
+        try {
+            String moduleName = names.getModuleName(JAVA_BASE, false);
+            assertEquals("java.base", moduleName);  //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(JAVA_BASE, false);
+            assertEquals("java.base", moduleName);  //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(JVMCI, false);
+            assertEquals("jdk.internal.vm.ci", moduleName); //NOI18N
+            assertTrue(th.isCalculated());
+            th.reset();
+            moduleName = names.getModuleName(JVMCI, false);
+            assertEquals("jdk.internal.vm.ci", moduleName); //NOI18N
+            assertFalse(th.isCalculated());
+            th.reset();
+        } finally {
+            th.unregister();
+        }
+    }
+
+    private void fakeIndex(
+            @NonNull final FileObject srcRoot,
+            @NonNull final URL binRoot,
+            @NullAllowed final String moduleName) throws IOException {
+        ClassIndexImpl q = ClassIndexManager.getDefault().createUsagesQuery(srcRoot.toURL(), true, ClassIndexEventsTransaction.create(true));
+        q.setState(ClassIndexImpl.State.INITIALIZED);
+        JavaIndex.setAttribute(srcRoot.toURL(), JavaIndex.ATTR_MODULE_NAME, moduleName);
+        ModuleNames.getInstance().reset(binRoot);
+    }
+
+    private Supplier<FileObject> writeFile(
+            @NonNull final FileObject folder,
+            @NonNull final String name,
+            @NonNull final Supplier<String> content) {
+        return () -> {
+            try {
+                final FileObject file = FileUtil.createData(folder, name);
+                try (PrintWriter out = new PrintWriter(new OutputStreamWriter(file.getOutputStream(), "UTF-8"))) {  //NOI18N
+                    out.println(content.get());
+                }
+                return file;
+            } catch (IOException ioe) {
+                throw new RuntimeException(ioe);
+            }
+        };
+    }
+
+    @NonNull
+    private FileObject rename(
+            @NonNull final FileObject root,
+            @NonNull final String name) throws IOException {
+        final String simpleName = FileObjects.stripExtension(name);
+        final String ext = FileObjects.getExtension(name);
+        final FileObject file = FileUtil.getArchiveFile(root);
+        final FileLock lck = file.lock();
+        try {
+            file.rename(lck, simpleName, ext);
+        } finally {
+            lck.releaseLock();
+        }
+        return FileUtil.getArchiveRoot(file);
+    }
+
+    @NonNull
+    private final Supplier<FileObject> jar(
+            @NonNull final FileObject folder,
+            @NonNull final String name,
+            @NullAllowed final Supplier<Collection<Pair<String,byte[]>>> content) {
+        return () -> {
+            try {
+                final FileObject zf = FileUtil.createData(folder, name);
+                final Map<String,byte[]> files = new HashMap<>();
+                Manifest mf = new Manifest();
+                if (content != null) {
+                    for (Pair<String,byte[]> c : content.get()) {
+                        if (RES_MANIFEST.equals(c.first())) {
+                            mf = new Manifest(new ByteArrayInputStream(c.second()));
+                        } else {
+                            files.put(c.first(), c.second());
+                        }
+                    }
+                }
+                try (final JarOutputStream out = new JarOutputStream(zf.getOutputStream(), mf)) {
+                    out.setComment("Test zip file");
+                    for (Map.Entry<String,byte[]> file : files.entrySet()) {
+                        final String path = file.getKey();
+                        final byte[] data = file.getValue();
+                        out.putNextEntry(new ZipEntry(path));
+                        out.write(data, 0, data.length);
+                        out.closeEntry();
+                    }
+                }
+                return zf;
+            } catch (IOException ioe) {
+                throw new RuntimeException(ioe);
+            }
+        };
+    }
+
+    @NonNull
+    private static Supplier<Pair<Boolean,String>> moduleInfoJava(
+            @NonNull final String moduleName,
+            @NonNull final Iterable<String> exports) {
+        return () -> {
+            final StringBuilder info = new StringBuilder();
+            info.append("module ")          //NOI18N
+                    .append(moduleName)
+                    .append(" {\n");        //NOI18N
+            for (String pkg : exports) {
+                info.append("exports ")     //NO18N
+                        .append(pkg)
+                        .append(";\n");     //NOI18N
+            }
+            info.append("}\n");             //NOI18N
+            return Pair.of(
+                    "java.base".equals(moduleName), //NOI18N
+                    info.toString());
+        };
+    }
+
+    @NonNull
+    private static Supplier<byte[]> moduleInfoClz(
+            @NonNull final Supplier<Pair<Boolean,String>> moduleInfoJava) {
+        return () -> {
+            try {
+                final JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
+                final List<String> opts = new ArrayList<>();
+                opts.add("-source");        //NOI18N
+                opts.add("9");              //NOI18N
+                opts.add("-target");        //NOI18N
+                opts.add("9");              //NOI18N
+                final Pair<Boolean,String> p = moduleInfoJava.get();
+                final JavaFileObject moduleInfo = FileObjects.memoryFileObject(
+                        "",                 //NOI18N
+                        "module-info.java", //NOI18N
+                        p.second());
+                final JavaFileManager fm = new MemFM(p.first());
+                final JavacTask task = (JavacTask) jc.getTask(null, fm, null, opts, null, Collections.singleton(moduleInfo));
+                final Iterator<? extends JavaFileObject> res = task.generate().iterator();
+                final JavaFileObject fo = res.hasNext() ?
+                        res.next() :
+                        null;
+                if (fo != null) {
+                    try (InputStream in = fo.openInputStream()) {
+                        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+                        FileUtil.copy(in, out);
+                        out.close();
+                        return out.toByteArray();
+                    }
+                } else {
+                    return null;
+                }
+            } catch (IOException ioe) {
+                throw new RuntimeException(ioe);
+            }
+        };
+    }
+
+    @ClientCodeWrapper.Trusted
+    private static final class MemFM implements JavaFileManager {
+        private final boolean bootstrap;
+        private final Supplier<byte[]> javaBaseModInfo;
+
+        MemFM(boolean bootstrap) {
+            this.bootstrap = bootstrap;
+            javaBaseModInfo = bootstrap ?
+                    null :
+                    moduleInfoClz(moduleInfoJava("java.base", Arrays.asList("java.lang","java.io"))); //NOI18N
+        }
+
+        @Override
+        public ClassLoader getClassLoader(Location location) {
+            return null;
+        }
+
+        @Override
+        public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                return bootstrap ?
+                        listModuleContent(
+                                (ModLoc) listLocationsForModules(StandardLocation.SYSTEM_MODULES).iterator().next().iterator().next(),
+                                packageName,
+                                kinds) :
+                        Collections.emptyList();
+            } else if (location instanceof ModLoc) {
+                return listModuleContent((ModLoc)location, packageName, kinds);
+            } else {
+                return Collections.emptyList();
+            }
+        }
+
+        @Override
+        public String inferBinaryName(Location location, JavaFileObject file) {
+            if (file instanceof InferableJavaFileObject) {
+                return ((InferableJavaFileObject)file).inferBinaryName();
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isSameFile(javax.tools.FileObject a, javax.tools.FileObject b) {
+            return a.toUri().equals(b.toUri());
+        }
+
+        @Override
+        public boolean handleOption(String current, Iterator<String> remaining) {
+            return false;
+        }
+
+        @Override
+        public boolean hasLocation(Location location) {
+            return location == StandardLocation.CLASS_OUTPUT ||
+                    location == StandardLocation.SYSTEM_MODULES;
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
+            return null;
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, javax.tools.FileObject sibling) throws IOException {
+            return new MemJFO(className);
+        }
+
+        @Override
+        public javax.tools.FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+            return null;
+        }
+
+        @Override
+        public javax.tools.FileObject getFileForOutput(Location location, String packageName, String relativeName, javax.tools.FileObject sibling) throws IOException {
+            return null;
+        }
+
+        @Override
+        public void flush() throws IOException {
+        }
+
+        @Override
+        public void close() throws IOException {
+        }
+
+        @Override
+        public int isSupportedOption(String option) {
+            return -1;
+        }
+
+        @Override
+        public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                return Collections.emptySet();
+            } else if (location == StandardLocation.SYSTEM_MODULES) {
+                final ClassPath cp = JavaPlatform.getDefault().getBootstrapLibraries();
+                Collection<? extends URL> javaBase = findJavaBase(cp);
+                if (javaBase.isEmpty()) {
+                    javaBase = fakeJavaBase(cp);
+                }
+                return Collections.singleton(Collections.singleton(new ModLoc(
+                        StandardLocation.SYSTEM_MODULES,
+                        "java.base",    //NOI18N
+                        javaBase)));
+            } else {
+                return Collections.emptySet();
+            }
+        }
+
+
+        @Override
+        public String inferModuleName(Location location) throws IOException {
+            return location instanceof ModLoc ?
+                    ((ModLoc)location).getModuleName() :
+                    null;
+        }
+
+        @NonNull
+        private Iterable<JavaFileObject> listModuleContent(
+                @NonNull final ModLoc modLoc,
+                @NonNull final String packageName,
+                @NonNull final Set<JavaFileObject.Kind> kinds) throws IOException {
+            final CachingArchiveProvider cap = CachingArchiveProvider.getDefault();
+            final Collection<JavaFileObject> res = new ArrayList<>();
+            if (javaBaseModInfo != null && "java.base".equals(modLoc.getModuleName()) && packageName.isEmpty()) {
+                final JavaFileObject jfo = new MemJFO("module-info");
+                try(OutputStream out = jfo.openOutputStream()) {
+                    final byte[] data = javaBaseModInfo.get();
+                    out.write(data, 0, data.length);
+                }
+                res.add(jfo);
+            }
+            for (URL url : modLoc.getRoots()) {
+                final Archive ca = cap.getArchive(url, false);
+                if (ca != null) {
+                    StreamSupport.stream(
+                            ca.getFiles(FileObjects.convertPackage2Folder(packageName), null, kinds, null, false).spliterator(),
+                            false)
+                            .forEach(res::add);
+                }
+            }
+            return res;
+        }
+
+        @NonNull
+        private static Collection<? extends URL> findJavaBase(@NonNull final ClassPath cp) {
+            return cp.entries().stream()
+                    .map(ClassPath.Entry::getURL)
+                    .filter((u) -> u.getProtocol().equals(FileObjects.PROTO_NBJRT) && u.toString().endsWith("/java.base/")) //NOI18N
+                    .map(Collections::singleton)
+                    .findAny()
+                    .orElse(Collections.emptySet());
+        }
+
+        @NonNull
+        private static Collection<? extends URL> fakeJavaBase(@NonNull final ClassPath cp) {
+            return cp.entries().stream()
+                    .map(ClassPath.Entry::getURL)
+                    .collect(Collectors.toList());
+        }
+
+        private static final class ModLoc implements Location {
+            private final Location base;
+            private final String modName;
+            private final Collection<? extends URL> roots;
+
+            ModLoc(
+                    @NonNull final Location base,
+                    @NonNull final String modName,
+                    @NonNull final Collection<? extends URL> roots) {
+                this.base = base;
+                this.modName = modName;
+                this.roots = roots;
+            }
+
+            @NonNull
+            Location getBase() {
+                return base;
+            }
+
+            @NonNull
+            String getModuleName() {
+                return modName;
+            }
+
+            @NonNull
+            Collection<? extends URL> getRoots() {
+                return roots;
+            }
+
+            @Override
+            public String getName() {
+                return getModuleName();
+            }
+
+            @Override
+            public boolean isOutputLocation() {
+                return false;
+            }
+
+            @Override
+            public boolean isModuleOrientedLocation() {
+                return false;
+            }
+        }
+    }
+
+    @ClientCodeWrapper.Trusted
+    private static final class MemJFO implements InferableJavaFileObject {
+        private final String fqn;
+        private byte[] data;
+        private long lm = -1L;
+
+        MemJFO(@NonNull final String fqn) {
+            this.fqn = fqn;
+        }
+
+        @Override
+        public Kind getKind() {
+            return Kind.CLASS;
+        }
+
+        @Override
+        public boolean isNameCompatible(String simpleName, Kind kind) {
+            return kind == Kind.CLASS && FileObjects.getBaseName(fqn, '.').equals(simpleName);
+        }
+
+        @Override
+        public NestingKind getNestingKind() {
+            return null;
+        }
+
+        @Override
+        public Modifier getAccessLevel() {
+            return null;
+        }
+
+        @Override
+        public URI toUri() {
+            try {
+                return new URI(FileObjects.convertPackage2Folder(fqn));
+            } catch (URISyntaxException e) {
+               throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public String getName() {
+            return FileObjects.convertPackage2Folder(fqn);
+        }
+
+        @Override
+        public InputStream openInputStream() throws IOException {
+            return new ByteArrayInputStream(data);
+        }
+
+        @Override
+        public OutputStream openOutputStream() throws IOException {
+            return new ByteArrayOutputStream() {
+                @Override
+                public void close() throws IOException {
+                    super.close();
+                    data = toByteArray();
+                    lm = System.currentTimeMillis();
+                }
+            };
+        }
+
+        @Override
+        public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+            return null;
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+            return null;
+        }
+
+        @Override
+        public Writer openWriter() throws IOException {
+            return null;
+        }
+
+        @Override
+        public long getLastModified() {
+            return lm;
+        }
+
+        @Override
+        public boolean delete() {
+            return true;
+        }
+
+        @Override
+        public String inferBinaryName() {
+            return getName();
+        }
+    }
+
+    private static final class TraceHandler extends Handler {
+        private static final String MSG_NO_CACHE = "No cache for: {0}"; //NOI18N
+
+        private final Logger logger;
+        private final Level origLevel;
+        private boolean calculated;
+
+
+        private TraceHandler(
+                @NonNull final Logger logger,
+                @NonNull final Level origLevel) {
+            this.logger = logger;
+            this.origLevel = origLevel;
+        }
+
+        boolean isCalculated() {
+            return calculated;
+        }
+
+        void reset() {
+            calculated = false;
+        }
+
+        void unregister() {
+            this.logger.setLevel(origLevel);
+            this.logger.removeHandler(this);
+        }
+
+        static TraceHandler register() {
+            final Logger l = Logger.getLogger(ModuleNames.class.getName());
+            final Level origLevel = l.getLevel();
+            final TraceHandler th = new TraceHandler(l, origLevel);
+            l.setLevel(Level.FINE);
+            l.addHandler(th);
+            return th;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            final String message = record.getMessage();
+            switch (message) {
+                case MSG_NO_CACHE:
+                    calculated = true;
+                    break;
+            }
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    }
+
+    public static final class BinAndSrc implements BinaryForSourceQueryImplementation, SourceForBinaryQueryImplementation2 {
+        private final Map<URL,Reference<BinR>> emmittedBinRs;
+        private final Map<URL,Reference<SrcR>> emmittedSrcRs;
+        private volatile Pair<URL,URL> root;
+
+        public BinAndSrc() {
+            this.emmittedBinRs = new HashMap<>();
+            this.emmittedSrcRs = new HashMap<>();
+        }
+
+        @CheckForNull
+        static BinAndSrc getInstance() {
+            return Lookup.getDefault().lookup(BinAndSrc.class);
+        }
+
+        void register(
+                @NonNull final URL srcRoot,
+                @NonNull final URL binRoot) {
+            this.root = Pair.of(srcRoot, binRoot);
+            emmittedSrcRs.values().stream()
+                    .map(Reference::get)
+                    .filter((r) -> r != null)
+                    .forEach(SrcR::changed);
+            emmittedBinRs.values().stream()
+                    .map(Reference::get)
+                    .filter((r) -> r != null)
+                    .forEach(BinR::changed);
+        }
+
+        @Override
+        public BinaryForSourceQuery.Result findBinaryRoots(URL sourceRoot) {
+            final Reference<BinR> rr = emmittedBinRs.get(sourceRoot);
+            BinR r;
+            if (rr != null && (r=rr.get()) != null) {
+                return r;
+            }
+            final Pair<URL,URL> current = root;
+            if (current != null && current.first().equals(sourceRoot)) {
+                r = new BinR(current.first());
+                emmittedBinRs.put(sourceRoot, new WeakReference<>(r));
+                return r;
+            }
+            return null;
+        }
+
+        @Override
+        public SourceForBinaryQueryImplementation2.Result findSourceRoots2(URL binaryRoot) {
+            final Reference<SrcR> rr = emmittedSrcRs.get(binaryRoot);
+            SrcR r;
+            if (rr != null && (r=rr.get()) != null) {
+                return r;
+            }
+            final Pair<URL,URL> current = root;
+            if (current != null && current.second().equals(binaryRoot)) {
+                r = new SrcR(current.second());
+                emmittedSrcRs.put(binaryRoot, new WeakReference<>(r));
+                return r;
+            }
+            return null;
+        }
+
+        @Override
+        public SourceForBinaryQuery.Result findSourceRoots(URL binaryRoot) {
+            return findSourceRoots2(binaryRoot);
+        }
+
+        private final class BinR implements BinaryForSourceQuery.Result {
+            private final URL src;
+            private final ChangeSupport listeners;
+
+            BinR(
+                    @NonNull final URL src) {
+                this.src = src;
+                this.listeners = new ChangeSupport(this);
+            }
+
+            @Override
+            public URL[] getRoots() {
+                final Pair<URL,URL> current = root;
+                if (current != null && src.equals(current.first())) {
+                    return new URL[] {current.second()};
+                }
+                return new URL[0];
+            }
+
+            void changed() {
+                listeners.fireChange();
+            }
+
+            @Override
+            public void addChangeListener(ChangeListener l) {
+                listeners.addChangeListener(l);
+            }
+
+            @Override
+            public void removeChangeListener(ChangeListener l) {
+                listeners.removeChangeListener(l);
+            }
+        }
+
+        private final class SrcR implements SourceForBinaryQueryImplementation2.Result {
+            private final URL bin;
+            private final ChangeSupport listeners;
+
+            SrcR(
+                    @NonNull final URL bin) {
+                this.bin = bin;
+                this.listeners = new ChangeSupport(this);
+            }
+
+            @Override
+            public boolean preferSources() {
+                return true;
+            }
+
+            @Override
+            public FileObject[] getRoots() {
+                final Pair<URL,URL> current = root;
+                if (current != null && bin.equals(current.second())) {
+                    final URL src = current.first();
+                    final FileObject srcFo = URLMapper.findFileObject(src);
+                    if (srcFo != null) {
+                        return new FileObject[] {srcFo};
+                    }
+                }
+                return new FileObject[0];
+            }
+
+            void changed() {
+                listeners.fireChange();
+            }
+
+            @Override
+            public void addChangeListener(ChangeListener l) {
+                this.listeners.addChangeListener(l);
+            }
+
+            @Override
+            public void removeChangeListener(ChangeListener l) {
+                this.listeners.removeChangeListener(l);
+            }
+        }
+    }
+
+    public static final class NBJRTStreamHandlerFactory implements URLStreamHandlerFactory {
+
+        @Override
+        public URLStreamHandler createURLStreamHandler(String protocol) {
+            if ("nbjrt".equals(protocol)) { //NOI18N
+                return new NBJRTURLStreamHandler();
+            }
+            return null;
+        }
+
+        private static class NBJRTURLStreamHandler extends URLStreamHandler {
+
+            @Override
+            protected URLConnection openConnection(URL u) throws IOException {
+                //Not needed
+                return null;
+            }
+        }
+    }
+
+    public static final class AutomaticModuleName implements CompilerOptionsQueryImplementation {
+
+        private Map<FileObject, R> roots = new HashMap<>();
+
+        @Override
+        public Result getOptions(FileObject file) {
+            for (Map.Entry<FileObject,R> e : roots.entrySet()) {
+                if (e.getKey().equals(file) || FileUtil.isParentOf(e.getKey(), file)) {
+                    return e.getValue();
+                }
+            }
+            return null;
+        }
+
+        void register(
+                @NonNull final FileObject root,
+                @NullAllowed final String moduleName) {
+            R r = roots.get(root);
+            if (r == null) {
+                r = new R();
+                roots.put(root, r);
+            }
+            r.setModuleName(moduleName);
+        }
+
+        @CheckForNull
+        public static AutomaticModuleName getInstance() {
+            return Lookup.getDefault().lookup(AutomaticModuleName.class);
+        }
+
+        private static class R extends CompilerOptionsQueryImplementation.Result {
+            private final ChangeSupport listeners;
+            private String moduleName;
+
+            private R() {
+                this.listeners = new ChangeSupport(this);
+            }
+
+            void setModuleName(@NullAllowed final String moduleName) {
+                this.moduleName = moduleName;
+                this.listeners.fireChange();
+            }
+
+            @Override
+            public List<? extends String> getArguments() {
+                if (moduleName != null) {
+                    return Collections.singletonList(String.format(
+                            "-XDautomatic-module-name:%s",  //NOI18N
+                            moduleName
+                    ));
+                } else {
+                    return Collections.emptyList();
+                }
+            }
+
+            @Override
+            public void addChangeListener(ChangeListener listener) {
+                this.listeners.addChangeListener(listener);
+            }
+
+            @Override
+            public void removeChangeListener(ChangeListener listener) {
+                this.listeners.removeChangeListener(listener);
+            }
+        }
+    }
+}
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/classpath/CacheSourceForBinaryQueryImplTest.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/classpath/CacheSourceForBinaryQueryImplTest.java
index f154f4dde..1aaab87ca 100644
--- a/java.source.base/test/unit/src/org/netbeans/modules/java/source/classpath/CacheSourceForBinaryQueryImplTest.java
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/classpath/CacheSourceForBinaryQueryImplTest.java
@@ -71,7 +71,7 @@ public CacheSourceForBinaryQueryImplTest(String testName) {
 
         beginTx();
 
-        this.cpInfo = ClasspathInfoAccessor.getINSTANCE().create(bootPath,ClassPath.EMPTY,compilePath,ClassPath.EMPTY,ClassPath.EMPTY,srcPath,ClassPath.EMPTY,null,true,false,false,false,null);
+        this.cpInfo = ClasspathInfoAccessor.getINSTANCE().create(bootPath,ClassPath.EMPTY,compilePath,ClassPath.EMPTY,ClassPath.EMPTY,srcPath,ClassPath.EMPTY,null,true,false,false,false,false,null);
         this.sfbq = new CacheSourceForBinaryQueryImpl ();
     }
 
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ClasspathInfoTest.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ClasspathInfoTest.java
index e63a8ea1c..754982091 100644
--- a/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ClasspathInfoTest.java
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ClasspathInfoTest.java
@@ -194,7 +194,7 @@ public void testMemoryFileManager () throws Exception {
         createJavaFile(scp.getRoots()[0], "org/me/Lib.java", "package org.me;\n class Lib {}\n");
         TransactionContext tx = TransactionContext.beginStandardTransaction(scp.getRoots()[0].toURL(), true, true, false);
         try {
-            final ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create( bootPath, ClassPath.EMPTY, classPath, ClassPath.EMPTY, ClassPath.EMPTY, scp, ClassPath.EMPTY, null, true, true, true, false, null);
+            final ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create( bootPath, ClassPath.EMPTY, classPath, ClassPath.EMPTY, ClassPath.EMPTY, scp, ClassPath.EMPTY, null, true, true, true, false, false, null);
             final JavaFileManager fm = ClasspathInfoAccessor.getINSTANCE().createFileManager(cpInfo, null);
             Iterable<JavaFileObject> jfos = fm.list(StandardLocation.SOURCE_PATH, "org.me", EnumSet.of(JavaFileObject.Kind.SOURCE), false);
             assertEquals (new String[] {"org.me.Lib"}, jfos, fm);
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ModuleOraculumTest.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ModuleOraculumTest.java
index b54a4177b..71cbed4ca 100644
--- a/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ModuleOraculumTest.java
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/ModuleOraculumTest.java
@@ -96,7 +96,7 @@ public void testOraculumLibrarySourceWithRoot() {
         assertNotNull(impl);
         final Options opts = Options.instance(impl.getContext());
         assertNotNull(opts);
-        assertEquals("Test", opts.get("-Xmodule:"));    //NOI18N
+        assertEquals("Test", opts.get("-XD-Xmodule:"));    //NOI18N
     }
 
     public void testOraculumLibrarySourceWithoutRootWithSourcePath() {
@@ -117,7 +117,7 @@ public void testOraculumLibrarySourceWithoutRootWithSourcePath() {
         assertNotNull(impl);
         final Options opts = Options.instance(impl.getContext());
         assertNotNull(opts);
-        assertEquals("Test", opts.get("-Xmodule:"));    //NOI18N
+        assertEquals("Test", opts.get("-XD-Xmodule:"));    //NOI18N
     }
 
     public void testOraculumLibrarySourceWithoutRootWithoutSourcePath() {
@@ -134,7 +134,7 @@ public void testOraculumLibrarySourceWithoutRootWithoutSourcePath() {
         assertNotNull(impl);
         final Options opts = Options.instance(impl.getContext());
         assertNotNull(opts);
-        assertEquals("Test", opts.get("-Xmodule:"));    //NOI18N
+        assertEquals("Test", opts.get("-XD-Xmodule:"));    //NOI18N
     }
 
     public void testOraculumLibrarySourceNoModuleInfo() throws IOException {
@@ -152,7 +152,7 @@ public void testOraculumLibrarySourceNoModuleInfo() throws IOException {
         assertNotNull(impl);
         final Options opts = Options.instance(impl.getContext());
         assertNotNull(opts);
-        assertNull(opts.get("-Xmodule:"));    //NOI18N
+        assertNull(opts.get("-XD-Xmodule:"));    //NOI18N
     }
 
     public void testOraculumProjectSource() throws IOException {
@@ -170,13 +170,13 @@ public void testOraculumProjectSource() throws IOException {
         assertNotNull(impl);
         final Options opts = Options.instance(impl.getContext());
         assertNotNull(opts);
-        assertNull(opts.get("-Xmodule:"));    //NOI18N
+        assertNull(opts.get("-XD-Xmodule:"));    //NOI18N
     }
 
     public void testOraculumLibrarySourceWithRootExpliciteXModule() throws IOException {
         Lookup.getDefault().lookup(COQ.class)
                 .forRoot(root1)
-                .apply("-Xmodule:SomeModule");  //NOI18N
+                .apply("-XD-Xmodule:SomeModule");  //NOI18N
         final ClasspathInfo cpInfo = new ClasspathInfo.Builder(ClassPath.EMPTY).build();
         final JavacParser parser = new JavacParser(Collections.emptyList(), true);
         final JavacTaskImpl impl = JavacParser.createJavacTask(
@@ -190,7 +190,7 @@ public void testOraculumLibrarySourceWithRootExpliciteXModule() throws IOExcepti
         assertNotNull(impl);
         final Options opts = Options.instance(impl.getContext());
         assertNotNull(opts);
-        assertEquals("SomeModule", opts.get("-Xmodule:"));    //NOI18N
+        assertEquals("SomeModule", opts.get("-XD-Xmodule:"));    //NOI18N
     }
 
 
@@ -220,7 +220,7 @@ public void testRootCache() {
                     null,
                     null,
                     false);
-            assertEquals("Test", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Test", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             List<? extends FileObject> roots = h.getRoots();
             assertEquals(1, roots.size());
             assertEquals(root1, roots.get(0));
@@ -233,7 +233,7 @@ public void testRootCache() {
                     null,
                     null,
                     false);
-            assertEquals("Test", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Test", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             roots = h.getRoots();
             assertEquals(0, roots.size());
             impl = JavacParser.createJavacTask(
@@ -244,7 +244,7 @@ public void testRootCache() {
                     null,
                     null,
                     false);
-            assertEquals("Next", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Next", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             roots = h.getRoots();
             assertEquals(1, roots.size());
             assertEquals(root2, roots.get(0));
@@ -271,7 +271,7 @@ public void testModuleNameCache() {
                     null,
                     null,
                     false);
-            assertEquals("Test", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Test", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             List<? extends String> names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals("Test", names.get(0)); //NOI18N
@@ -284,7 +284,7 @@ public void testModuleNameCache() {
                     null,
                     null,
                     false);
-            assertEquals("Test", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Test", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(0, names.size());
             impl = JavacParser.createJavacTask(
@@ -295,7 +295,7 @@ public void testModuleNameCache() {
                     null,
                     null,
                     false);
-            assertEquals("Next", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Next", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals("Next", names.get(0)); //NOI18N
@@ -322,7 +322,7 @@ public void testModuleNameCache_ModuleInfoUpdated() throws IOException {
                     null,
                     null,
                     false);
-            assertEquals("Test", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Test", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             List<? extends String> names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals("Test", names.get(0)); //NOI18N
@@ -336,7 +336,7 @@ public void testModuleNameCache_ModuleInfoUpdated() throws IOException {
                     null,
                     null,
                     false);
-            assertEquals("TestUpdated", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("TestUpdated", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals("TestUpdated", names.get(0)); //NOI18N
@@ -349,7 +349,7 @@ public void testModuleNameCache_ModuleInfoUpdated() throws IOException {
                     null,
                     null,
                     false);
-            assertEquals("TestUpdated", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("TestUpdated", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(0, names.size());
         } finally {
@@ -375,7 +375,7 @@ public void testModuleNameCache_ModuleInfoDeleted() throws IOException {
                     null,
                     null,
                     false);
-            assertEquals("Test", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("Test", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             List<? extends String> names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals("Test", names.get(0)); //NOI18N
@@ -389,7 +389,7 @@ public void testModuleNameCache_ModuleInfoDeleted() throws IOException {
                     null,
                     null,
                     false);
-            assertNull(Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertNull(Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals(null, names.get(0));
@@ -402,7 +402,7 @@ public void testModuleNameCache_ModuleInfoDeleted() throws IOException {
                     null,
                     null,
                     false);
-            assertNull(Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertNull(Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(0, names.size());
         } finally {
@@ -429,7 +429,7 @@ public void testModuleNameCache_ModuleInfoCreated() throws IOException {
                     null,
                     null,
                     false);
-            assertNull(Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertNull(Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             List<? extends String> names = h.getModuleNames();
             assertEquals(1, names.size());
             assertNull(names.get(0));
@@ -443,7 +443,7 @@ public void testModuleNameCache_ModuleInfoCreated() throws IOException {
                     null,
                     null,
                     false);
-            assertEquals("TestNew", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("TestNew", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(1, names.size());
             assertEquals("TestNew", names.get(0));  //NOI18N
@@ -456,7 +456,7 @@ public void testModuleNameCache_ModuleInfoCreated() throws IOException {
                     null,
                     null,
                     false);
-            assertEquals("TestNew", Options.instance(impl.getContext()).get("-Xmodule:"));    //NOI18N
+            assertEquals("TestNew", Options.instance(impl.getContext()).get("-XD-Xmodule:"));    //NOI18N
             names = h.getModuleNames();
             assertEquals(0, names.size());
         } finally {
diff --git a/jumpto/nbproject/project.properties b/jumpto/nbproject/project.properties
index 34a65dbb6..84d020d21 100644
--- a/jumpto/nbproject/project.properties
+++ b/jumpto/nbproject/project.properties
@@ -18,7 +18,7 @@
 javac.compilerargs=-Xlint:unchecked
 javac.source=1.8
 nbm.module.author=Andrei Badea, Petr Hrebejk
-spec.version.base=1.54.0
+spec.version.base=1.55.0
 
 test.config.stableBTD.includes=**/*Test.class
 test.config.stableBTD.excludes=\
diff --git a/jumpto/src/org/netbeans/modules/jumpto/EntityComparator.java b/jumpto/src/org/netbeans/modules/jumpto/EntityComparator.java
index ae2b33ab6..720b0c157 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/EntityComparator.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/EntityComparator.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
+import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.api.project.ui.OpenProjects;
@@ -109,6 +110,49 @@ protected int compareProjects(String p1Name, String p2Name) {
         return 0;
     }
 
+    public String levenshteinPrefix (
+            @NonNull String name,
+            @NonNull String text,
+            final boolean caseSensitive) {
+        if (!caseSensitive) {
+            name = name.toLowerCase();
+            text = text.toLowerCase();
+        }
+        int index = 0;
+        int i = 0;
+        for (;i < name.length() && index < text.length(); i++) {
+            if (name.charAt(i) == text.charAt(index)) {
+                index++;
+            }
+        }
+        return name.substring(0,i);
+    }
+
+    public final int levenshteinDistance(
+            @NonNull String str1,
+            @NonNull String str2,
+            final boolean caseSensitive) {
+        if (!caseSensitive) {
+            str1 = str1.toLowerCase();
+            str2 = str2.toLowerCase();
+        }
+        int[][] distance = new int[str1.length() + 1][str2.length() + 1];
+
+        for (int i = 0; i <= str1.length(); i++)
+            distance[i][0] = i;
+        for (int j = 1; j <= str2.length(); j++)
+            distance[0][j] = j;
+
+        for (int i = 1; i <= str1.length(); i++)
+            for (int j = 1; j <= str2.length(); j++)
+                distance[i][j] = minimum(
+                        distance[i - 1][j] + 1,
+                        distance[i][j - 1] + 1,
+                        distance[i - 1][j - 1] + ((str1.charAt(i - 1) == str2.charAt(j - 1)) ? 0 : 1));
+
+        return distance[str1.length()][str2.length()];
+    }
+
     /**
      * Returns a name of the main project (if any).
      * @return a name if main project exists, otherwise {@code null}.
@@ -132,4 +176,7 @@ public static String getMainProjectName() {
         return names;
     }
 
+    private static int minimum(int a, int b, int c) {
+        return Math.min(Math.min(a, b), c);
+    }
 }
diff --git a/jumpto/src/org/netbeans/modules/jumpto/common/DescriptorAccessor.java b/jumpto/src/org/netbeans/modules/jumpto/common/DescriptorAccessor.java
new file mode 100644
index 000000000..4e5a281e7
--- /dev/null
+++ b/jumpto/src/org/netbeans/modules/jumpto/common/DescriptorAccessor.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.jumpto.common;
+
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.spi.jumpto.support.Descriptor;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public abstract class DescriptorAccessor {
+
+    private static volatile DescriptorAccessor instance;
+
+    public static void setInstance(@NonNull final DescriptorAccessor anInstance) {
+        instance = anInstance;
+    }
+
+    @NonNull
+    public static synchronized DescriptorAccessor getInstance() {
+        if (instance == null) {
+            try {
+                Class.forName(Descriptor.class.getName(), true, DescriptorAccessor.class.getClassLoader());
+            } catch (ClassNotFoundException ex) {
+                throw new RuntimeException(ex);
+            }
+            assert instance != null;
+        }
+        return instance;
+    }
+
+    @CheckForNull
+    public abstract Object getAttribute(@NonNull Descriptor descriptor, @NonNull final String attribute);
+
+    public abstract void setAttribute(@NonNull Descriptor descriptor, @NonNull final String attribute, @NullAllowed Object value);
+}
diff --git a/jumpto/src/org/netbeans/modules/jumpto/common/ItemRenderer.java b/jumpto/src/org/netbeans/modules/jumpto/common/ItemRenderer.java
index 8f401f70f..e024d85d5 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/common/ItemRenderer.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/common/ItemRenderer.java
@@ -49,7 +49,7 @@
 import org.netbeans.api.editor.mimelookup.MimePath;
 import org.netbeans.api.editor.settings.FontColorSettings;
 import org.netbeans.modules.jumpto.EntityComparator;
-import org.netbeans.modules.jumpto.settings.HighlightingSettings;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.openide.awt.HtmlRenderer;
 import org.openide.util.ImageUtilities;
 import org.openide.util.Parameters;
@@ -122,7 +122,7 @@ public Builder setColorPreferedProject(@NullAllowed final ButtonModel colorPrefe
     private static final String SAMPLE_ITEM_ICON = "org/netbeans/modules/jumpto/type/sample.png";
     private static final int DARKER_COLOR_COMPONENT = 15;
     private static final int LIGHTER_COLOR_COMPONENT = 80;
-    private final HighlightingSettings.Mode highlightMode;
+    private final GoToSettings.HighlightingMode highlightMode;
     private final HighlightingNameFormatter nameFormater;
     private final String mainProjectName = EntityComparator.getMainProjectName();
     private final Convertor<T> convertor;
@@ -157,9 +157,9 @@ private ItemRenderer(
         this.caseSensitive = caseSensitive;
         this.colorPrefered = colorPrefered;
         this.convertor = convertor;
-        final HighlightingSettings hs = HighlightingSettings.getDefault();
-        highlightMode = hs.getMode();
-        nameFormater = createNameFormatter(hs.getType(), separatorPattern);
+        final GoToSettings hs = GoToSettings.getDefault();
+        highlightMode = hs.getHighlightingMode();
+        nameFormater = createNameFormatter(hs.getHighlightingType(), separatorPattern);
         this.jlName = new JLabel();
         resetNameLabel();
         Container container = list.getParent();
@@ -414,7 +414,7 @@ private String highlight(
     }
     
     private static HighlightingNameFormatter createNameFormatter(
-            @NonNull final HighlightingSettings.Type type,
+            @NonNull final GoToSettings.HighlightingType type,
             @NonNull final String separatorPattern) {
         switch (type) {
             case BACKGROUND:
diff --git a/jumpto/src/org/netbeans/modules/jumpto/common/Models.java b/jumpto/src/org/netbeans/modules/jumpto/common/Models.java
index 206c142bd..f98f699b4 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/common/Models.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/common/Models.java
@@ -91,6 +91,7 @@ private  Models() {
     public interface MutableListModel<T> extends ListModel<T> {
         public void add(@NonNull Collection<? extends T> values);
         public void remove (@NonNull Collection<? extends T> values);
+        public void clear();
     }
 
     public interface Filter<T> {
@@ -414,6 +415,16 @@ public void remove(Collection<? extends T> values) {
             } while (!success);
         }
 
+        @Override
+        public void clear() {
+            boolean success;
+            do {
+                final Pair<List<T>,List<T>> data = getData();
+                data.second().clear();
+                success = casData(data.first(), data.second());
+            } while (!success);
+        }
+
         @Override
         public void stateChanged(@NonNull final ChangeEvent e) {
             final Object source = e.getSource();
diff --git a/jumpto/src/org/netbeans/modules/jumpto/file/FileComarator.java b/jumpto/src/org/netbeans/modules/jumpto/file/FileComarator.java
index a008f8664..dd5dc9144 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/file/FileComarator.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/file/FileComarator.java
@@ -19,10 +19,13 @@
 
 package org.netbeans.modules.jumpto.file;
 
+import java.util.Objects;
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.modules.jumpto.EntityComparator;
+import org.netbeans.modules.jumpto.common.DescriptorAccessor;
 import org.netbeans.modules.jumpto.common.StateFullComparator;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.spi.jumpto.file.FileDescriptor;
 import org.openide.util.ChangeSupport;
 
@@ -37,17 +40,20 @@
  * @author Victor G. Vasilyev <vv...@netbeans.org>
  */
 //@NotThreadSave //Use from EDT
-public final class FileComarator extends EntityComparator<FileDescriptor> implements StateFullComparator<FileDescriptor>{
+public abstract class FileComarator extends EntityComparator<FileDescriptor> implements StateFullComparator<FileDescriptor> {
 
     private final ChangeSupport support;
-    private final boolean caseSensitive;
-    private boolean usePreferred;
+    protected final boolean caseSensitive;
+    protected final boolean preferOpPrjs;
+    protected boolean usePreferred;
 
-    public FileComarator(
+    private FileComarator(
             final boolean usePreferred,
-            final boolean caseSensitive ) {
+            final boolean caseSensitive,
+            final boolean preferOpPrjs) {
         this.caseSensitive = caseSensitive;
         this.usePreferred = usePreferred;
+        this.preferOpPrjs = preferOpPrjs;
         this.support = new ChangeSupport(this);
     }
 
@@ -63,41 +69,171 @@ void setUsePreferred(final boolean usePreferred) {
         }
     }
 
-    @Override
-    public int compare(FileDescriptor e1, FileDescriptor e2) {
-        // If prefered prefer prefered
-        if ( usePreferred ) {
-            FileProviderAccessor fpa = FileProviderAccessor.getInstance();
-            boolean isE1Curr = fpa.isFromCurrentProject(e1);
-            boolean isE2Curr = fpa.isFromCurrentProject(e2);
-            if (isE1Curr && !isE2Curr) {
-                return -1;
+    abstract void setText(@NonNull final String text);
+
+    public abstract int compare(FileDescriptor e1, FileDescriptor e2);
+
+    void fireChange() {
+        support.fireChange();
+    }
+
+    private static final class Alphabet extends FileComarator {
+
+        Alphabet(
+                final boolean usePreferred,
+                final boolean caseSensitive,
+                final boolean preferOpPrjs) {
+            super(usePreferred, caseSensitive, preferOpPrjs);
+        }
+
+        void setText(@NonNull final String text) {
+        }
+
+        @Override
+        public int compare(FileDescriptor e1, FileDescriptor e2) {
+            // If prefered prefer prefered
+            if ( usePreferred ) {
+                FileProviderAccessor fpa = FileProviderAccessor.getInstance();
+                boolean isE1Curr = fpa.isFromCurrentProject(e1);
+                boolean isE2Curr = fpa.isFromCurrentProject(e2);
+                if (isE1Curr && !isE2Curr) {
+                    return -1;
+                }
+                if (!isE1Curr && isE2Curr) {
+                    return 1;
+                }
+            }
+            int result;
+            // Containig project
+            if (preferOpPrjs) {
+                String e1projectName = e1.getProjectName();
+                String e2projectName = e2.getProjectName();
+                result = compareProjects(e1projectName, e2projectName);
+                if(result != 0) {
+                    return result; // e1projectName NOT equals to e2projectName
+                }
             }
-            if (!isE1Curr && isE2Curr) {
-                return 1;
+            // here: e1projectName equals to e2projectName
+            // File name
+            result = compare(e1.getFileName(), e2.getFileName(), caseSensitive);
+            if ( result != 0 ) {
+                return result;
             }
+            // Project name
+            result = compare(e1.getProjectName(), e2.getProjectName(), caseSensitive);
+            if ( result != 0 ) {
+                return result;
+            }
+            // Relative location
+            result = compare( e1.getOwnerPath(), e2.getOwnerPath(), caseSensitive);
+            return result;
         }
-        // Containig project
-        String e1projectName = e1.getProjectName();
-        String e2projectName = e2.getProjectName();
-        int result = compareProjects(e1projectName, e2projectName);
-        if(result != 0) {
-            return result; // e1projectName NOT equals to e2projectName
+    }
+
+    private static final class Levenshtein extends FileComarator {
+
+        private static final String ATTR_PATTERN = "Pattern";       //NOI18N
+        private static final String ATTR_LS_DIST = "LevenshteinDistance";   //NOI18N
+        private static final String ATTR_LS_TAIL = "LevenshteinTail";   //NOI18N
+
+        private String text;
+
+        Levenshtein(
+                final String text,
+                final boolean usePreferred,
+                final boolean caseSensitive,
+                final boolean preferOpPrjs) {
+            super(usePreferred, caseSensitive, preferOpPrjs);
+            this.text = text;
         }
-        // here: e1projectName equals to e2projectName
-        // File name
-        int r = compare(e1.getFileName(), e2.getFileName(), caseSensitive);
-        if ( r != 0 ) {
-            return r;
+
+        void setText(@NonNull final String text) {
+            final boolean fire = !Objects.equals(this.text, text);
+            this.text = text;
+            if (fire) {
+                fireChange();
+            }
         }
-        // Project name
-        r = compare(e1.getProjectName(), e2.getProjectName(), caseSensitive);
-        if ( r != 0 ) {
-            return r;
+
+        @Override
+        public int compare(FileDescriptor e1, FileDescriptor e2) {
+            if (usePreferred) {
+                FileProviderAccessor fpa = FileProviderAccessor.getInstance();
+                boolean isE1Curr = fpa.isFromCurrentProject(e1);
+                boolean isE2Curr = fpa.isFromCurrentProject(e2);
+                if (isE1Curr && !isE2Curr) {
+                    return -1;
+                }
+                if (!isE1Curr && isE2Curr) {
+                    return 1;
+                }
+            }
+            // Containig project
+            int result;
+            if (preferOpPrjs) {
+                String e1projectName = e1.getProjectName();
+                String e2projectName = e2.getProjectName();
+                result = compareProjects(e1projectName, e2projectName);
+                if(result != 0) {
+                    return result; // e1projectName NOT equals to e2projectName
+                }
+            }
+            int l1, l2, t1, t2;
+            Object d = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_LS_DIST);
+            Object t = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_LS_TAIL);
+            Object p = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_PATTERN);
+            if (d instanceof Integer && t instanceof Integer && text.equals(p)) {
+                l1 = (Integer) d;
+                t1 = (Integer) t;
+            } else {
+                final String fn = e1.getFileName();
+                final String prefix = levenshteinPrefix(fn, text, caseSensitive);
+                l1 = levenshteinDistance(prefix, text, caseSensitive);
+                t1 = fn.length() - prefix.length();
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_LS_DIST, l1);
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_LS_TAIL, t1);
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_PATTERN, text);
+            }
+            d = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_LS_DIST);
+            t = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_LS_TAIL);
+            p = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_PATTERN);
+            if (d instanceof Integer && t instanceof Integer && text.equals(p)) {
+                l2 = (Integer) d;
+                t2 = (Integer) t;
+            } else {
+                final String fn = e2.getFileName();
+                final String prefix = levenshteinPrefix(fn, text, caseSensitive);
+                l2 = levenshteinDistance(prefix, text, caseSensitive);
+                t2 = fn.length() - prefix.length();
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_LS_DIST, l2);
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_LS_TAIL, t2);
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_PATTERN, text);
+            }
+
+            result = l1 - l2;
+            if (result != 0) {
+                return result;
+            }
+            result = t1 - t2;
+            if (result != 0) {
+                return result;
+            }
+
+            // here: e1projectName equals to e2projectName
+            // File name
+            result = compare(e1.getFileName(), e2.getFileName(), caseSensitive);
+            if ( result != 0 ) {
+                return result;
+            }
+            // Project name
+            result = compare(e1.getProjectName(), e2.getProjectName(), caseSensitive);
+            if ( result != 0 ) {
+                return result;
+            }
+            // Relative location
+            result = compare( e1.getOwnerPath(), e2.getOwnerPath(), caseSensitive);
+            return result;
         }
-        // Relative location
-        r = compare( e1.getOwnerPath(), e2.getOwnerPath(), caseSensitive);
-        return r;
     }
 
     @Override
@@ -110,5 +246,21 @@ public void removeChangeListener(@NonNull final ChangeListener listener) {
         support.removeChangeListener(listener);
     }
 
+    @NonNull
+    public static FileComarator create(
+            @NonNull final GoToSettings.SortingType kind,
+            @NonNull final String text,
+            final boolean usePreferred,
+            final boolean caseSensitive,
+            final boolean preferOpPrjs) {
+        switch (kind) {
+            case LEXICOGRAPHIC:
+                return new Alphabet(usePreferred, caseSensitive, preferOpPrjs);
+            case LEVENSHTEIN:
+                return new Levenshtein(text, usePreferred, caseSensitive, preferOpPrjs);
+            default:
+                throw new IllegalArgumentException(String.valueOf(kind));
+        }
+    }
 }
 
diff --git a/jumpto/src/org/netbeans/modules/jumpto/file/FileSearchAction.java b/jumpto/src/org/netbeans/modules/jumpto/file/FileSearchAction.java
index 1a451510e..74bed1fb9 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/file/FileSearchAction.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/file/FileSearchAction.java
@@ -58,10 +58,12 @@
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ui.OpenProjects;
 import org.netbeans.editor.JumpList;
+import org.netbeans.modules.jumpto.EntityComparator;
 import org.netbeans.modules.jumpto.common.AbstractModelFilter;
 import org.netbeans.modules.jumpto.common.ItemRenderer;
 import org.netbeans.modules.jumpto.common.Models;
 import org.netbeans.modules.jumpto.common.Utils;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.modules.parsing.lucene.support.Queries;
 import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
 import org.netbeans.spi.jumpto.file.FileDescriptor;
@@ -216,6 +218,7 @@ public boolean setListModel(final FileSearchPanel panel, String text ) {
             final SearchType searchType = Utils.toSearchType(nameKind);
             if (currentSearch.isNarrowing(searchType, text, null, true)) {
                 itemsComparator.setUsePreferred(panel.isPreferedProject());
+                itemsComparator.setText(text);
                 filterFactory.setLineNumber(lineNr);
                 currentSearch.filter(
                         searchType,
@@ -250,9 +253,12 @@ private synchronized void invokeProviders() {
         }
         final String searchText = slidingTaskData.text;
         LOGGER.log(Level.FINE, "Calling providers for text: {0}", searchText);
-        itemsComparator = new FileComarator(
+        itemsComparator = FileComarator.create(
+                GoToSettings.getDefault().getSortingType(),
+                searchText,
                 panel.isPreferedProject(),
-                panel.isCaseSensitive());
+                panel.isCaseSensitive(),
+                GoToSettings.getDefault().isSortingPreferOpenProjects());
         final Models.MutableListModel baseListModel = Models.mutable(
                 itemsComparator,
                 currentSearch.resetFilter(),
diff --git a/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToSymbolWorker.java b/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToSymbolWorker.java
index 69e928877..ad2206807 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToSymbolWorker.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToSymbolWorker.java
@@ -27,9 +27,11 @@
 import java.util.List;
 import java.util.logging.Logger;
 import javax.swing.ListModel;
+import org.netbeans.modules.jumpto.EntityComparator;
 import org.netbeans.modules.jumpto.symbol.SymbolComparator;
 import org.netbeans.modules.jumpto.symbol.SymbolProviderAccessor;
 import org.netbeans.modules.jumpto.common.Models;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.spi.jumpto.symbol.SymbolDescriptor;
 import org.netbeans.spi.jumpto.symbol.SymbolProvider;
 import org.netbeans.spi.jumpto.type.SearchType;
@@ -126,7 +128,7 @@ public void cancel() {
         }
         if (!isCanceled) {
             //time = System.currentTimeMillis();
-            Collections.sort(items, new SymbolComparator());
+            Collections.sort(items, SymbolComparator.create(GoToSettings.SortingType.LEXICOGRAPHIC, text, false, true));
             //panel.setWarning(message[0]);
             //sort += System.currentTimeMillis() - time;
             //LOGGER.fine("PERF - " + " GSS:  " + gss + " GSB " + gsb + " CP: " + cp + " SFB: " + sfb + " GTN: " + gtn + "  ADD: " + add + "  SORT: " + sort );
diff --git a/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToTypeWorker.java b/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToTypeWorker.java
index 2bf14df44..c3f5b476f 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToTypeWorker.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/quicksearch/GoToTypeWorker.java
@@ -27,6 +27,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.netbeans.modules.jumpto.EntityComparator;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.modules.jumpto.type.TypeComparator;
 import org.netbeans.modules.jumpto.type.TypeProviderAccessor;
 import org.netbeans.spi.jumpto.type.SearchType;
@@ -74,7 +75,7 @@ public void cancel() {
         isCanceled = true;
     }
 
-    private List<? extends TypeDescriptor> getTypeNames(String text) {
+    private List<? extends TypeDescriptor> getTypeNames(final String text) {
         // Multiple providers: merge results
         List<TypeDescriptor> items = new ArrayList<TypeDescriptor>(128);
         List<TypeDescriptor> ccItems = new ArrayList<TypeDescriptor>(128);
@@ -106,7 +107,7 @@ public void cancel() {
         ts.addAll(items);
         items.clear();
         items.addAll(ts); //eliminate duplicates
-        Collections.sort(items, new TypeComparator());
+        Collections.sort(items, TypeComparator.create(GoToSettings.SortingType.LEXICOGRAPHIC, text, false, true));
         return items;
     }
 
diff --git a/jumpto/src/org/netbeans/modules/jumpto/settings/Bundle.properties b/jumpto/src/org/netbeans/modules/jumpto/settings/Bundle.properties
index 522cf2255..d7ba3e22c 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/settings/Bundle.properties
+++ b/jumpto/src/org/netbeans/modules/jumpto/settings/Bundle.properties
@@ -23,10 +23,15 @@ AD_Highlight_By=N/A
 NAME_NONE=Nothing
 NAME_ACTIVE=Selected Item
 NAME_ALL=All Items
-
 NAME_BACKGROUND=Background Color
 NAME_BOLD=Bold Font
+NAME_LEXICOGRAPHIC=Alphabet
+NAME_LEVENSHTEIN=Similarity
+
 JumpToPanel.jLabel3.toolTipText=
 
 AdvancedOption_DisplayName_GoTo=Go To
 AdvancedOption_Keywords_GoTo=goto,symbol,file,type
+JumpToPanel.orderByLabel.text=&Order by:
+JumpToPanel.jLabel4.text=Go To Dialog Sorting
+JumpToPanel.preferOpenPrj.text=Prefer Open &Projects
diff --git a/jumpto/src/org/netbeans/modules/jumpto/settings/GoToSettings.java b/jumpto/src/org/netbeans/modules/jumpto/settings/GoToSettings.java
new file mode 100644
index 000000000..2656475dc
--- /dev/null
+++ b/jumpto/src/org/netbeans/modules/jumpto/settings/GoToSettings.java
@@ -0,0 +1,261 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.jumpto.settings;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.prefs.Preferences;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.NbBundle;
+import org.openide.util.NbPreferences;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public final class GoToSettings {
+
+    public enum HighlightingMode {
+        NONE("none", NbBundle.getMessage(GoToSettings.class, "NAME_NONE")),
+        ACTIVE("active", NbBundle.getMessage(GoToSettings.class, "NAME_ACTIVE")),
+        ALL("all", NbBundle.getMessage(GoToSettings.class, "NAME_ALL"));
+
+        private static final Map<String, HighlightingMode> modesBySystemName;
+        static {
+            final Map<String,HighlightingMode> map = new HashMap<>();
+            for (HighlightingMode m : HighlightingMode.values()) {
+                map.put(m.getSystemName(), m);
+            }
+            modesBySystemName = Collections.unmodifiableMap(map);
+        }
+
+        private final String systemName;
+        private final String displayName;
+
+        private HighlightingMode(@NonNull String systemName, @NonNull String displayName) {
+            assert systemName != null;
+            assert displayName != null;
+            this.systemName = systemName;
+            this.displayName = displayName;
+        }
+
+        @NonNull
+        public String getDisplayName() {
+            return this.displayName;
+        }
+
+        @NonNull
+        String getSystemName() {
+            return this.systemName;
+        }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+
+        @NonNull
+        static HighlightingMode forSystemName(@NullAllowed final String sysName) {
+            HighlightingMode m = modesBySystemName.get(sysName);
+            if (m == null) {
+                m = getDefault();
+            }
+            return m;
+        }
+
+        @NonNull
+        private static HighlightingMode getDefault() {
+            return ACTIVE;
+        }
+    }
+
+    public enum HighlightingType {
+        BOLD("bold", NbBundle.getMessage(GoToSettings.class, "NAME_BOLD")),
+        BACKGROUND("background", NbBundle.getMessage(GoToSettings.class, "NAME_BACKGROUND"));
+
+        private static final Map<String,HighlightingType> typesBySystemName;
+        static {
+            Map<String,HighlightingType> map = new HashMap<>();
+            for (HighlightingType t : HighlightingType.values()) {
+                map.put(t.getSystemName(), t);
+            }
+            typesBySystemName = Collections.unmodifiableMap(map);
+        }
+
+        private final String systemName;
+        private final String displayName;
+
+        private HighlightingType(@NonNull String systemName, @NonNull String displayName) {
+            assert systemName != null;
+            assert displayName != null;
+            this.systemName = systemName;
+            this.displayName = displayName;
+        }
+
+        @NonNull
+        public String getDisplayName() {
+            return this.displayName;
+        }
+
+        @NonNull
+        String getSystemName() {
+            return this.systemName;
+        }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+
+        @NonNull
+        static HighlightingType forSystemName(@NullAllowed final String systemName) {
+            HighlightingType type = typesBySystemName.get(systemName);
+            if (type == null) {
+                type = getDefault();
+            }
+            return type;
+        }
+
+        @NonNull
+        private static HighlightingType getDefault() {
+            return BACKGROUND;
+        }
+    }
+
+    public enum SortingType {
+        LEXICOGRAPHIC("lexicographic", NbBundle.getMessage(GoToSettings.class, "NAME_LEXICOGRAPHIC")),  //NOI18N
+        LEVENSHTEIN("levenshtein", NbBundle.getMessage(GoToSettings.class, "NAME_LEVENSHTEIN"));        //NOI18N
+
+        private static final Map<String,SortingType> typesBySystemName;
+        static {
+            Map<String,SortingType> map = new HashMap<>();
+            for (SortingType t : SortingType.values()) {
+                map.put(t.getSystemName(), t);
+            }
+            typesBySystemName = Collections.unmodifiableMap(map);
+        }
+
+        private final String systemName;
+        private final String displayName;
+
+        private SortingType(@NonNull String systemName, @NonNull String displayName) {
+            assert systemName != null;
+            assert displayName != null;
+            this.systemName = systemName;
+            this.displayName = displayName;
+        }
+
+        @NonNull
+        public String getDisplayName() {
+            return this.displayName;
+        }
+
+        @NonNull
+        String getSystemName() {
+            return this.systemName;
+        }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+
+        @NonNull
+        static SortingType forSystemName(@NullAllowed final String systemName) {
+            SortingType type = typesBySystemName.get(systemName);
+            if (type == null) {
+                type = getDefault();
+            }
+            return type;
+        }
+
+        @NonNull
+        private static SortingType getDefault() {
+            return LEXICOGRAPHIC;
+        }
+    }
+
+    private static final String NODE_HIGHLIGHTING = "highlighting";     //NOI18N
+    private static final String NODE_SORTING = "sorting";               //NOI18N
+    private static final String KEY_HIGHLIGHTING_MODE = "mode";         //NOI18N
+    private static final String KEY_HIGHLIGHTING_TYPE = "type";         //NOI18N
+    private static final String KEY_SORTING_TYPE = "type";              //NOI18N
+    private static final String KEY_SORTING_PRJ = "preffer-projects";   //NOI18N
+    private static final GoToSettings INSTANCE = new GoToSettings();
+
+    private GoToSettings() {
+    }
+
+    @NonNull
+    public HighlightingMode getHighlightingMode() {
+        return HighlightingMode.forSystemName(getHighlightingNode().get(KEY_HIGHLIGHTING_MODE, null));
+    }
+
+    void setHighlightingMode(@NonNull final HighlightingMode mode) {
+        assert mode != null;
+        getHighlightingNode().put(KEY_HIGHLIGHTING_MODE, mode.getSystemName());
+    }
+
+    @NonNull
+    public HighlightingType getHighlightingType() {
+        return HighlightingType.forSystemName(getHighlightingNode().get(KEY_HIGHLIGHTING_TYPE, null));
+    }
+
+    void setHighlightingType(@NonNull final HighlightingType type) {
+        assert type != null;
+        getHighlightingNode().put(KEY_HIGHLIGHTING_TYPE, type.getSystemName());
+    }
+
+    @NonNull
+    public SortingType getSortingType() {
+        return SortingType.forSystemName(getSortingNode().get(KEY_SORTING_TYPE, null));
+    }
+
+    void setSortingType(@NonNull final SortingType type) {
+        assert type != null;
+        getSortingNode().put(KEY_SORTING_TYPE, type.getSystemName());
+    }
+
+    public boolean isSortingPreferOpenProjects() {
+        return getSortingNode().getBoolean(KEY_SORTING_PRJ, true);
+    }
+
+    public void setSortingPreferOpenProjects(final boolean value) {
+        getSortingNode().putBoolean(KEY_SORTING_PRJ, value);
+    }
+
+    @NonNull
+    private Preferences getHighlightingNode() {
+        final Preferences prefs = NbPreferences.forModule(GoToSettings.class);
+        return prefs.node(NODE_HIGHLIGHTING);
+    }
+
+    @NonNull
+    private Preferences getSortingNode() {
+        final Preferences prefs = NbPreferences.forModule(GoToSettings.class);
+        return prefs.node(NODE_SORTING);
+    }
+
+    @NonNull
+    public static GoToSettings getDefault() {
+        return INSTANCE;
+    }
+}
diff --git a/jumpto/src/org/netbeans/modules/jumpto/settings/HighlightingSettings.java b/jumpto/src/org/netbeans/modules/jumpto/settings/HighlightingSettings.java
deleted file mode 100644
index b4b2c734b..000000000
--- a/jumpto/src/org/netbeans/modules/jumpto/settings/HighlightingSettings.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.netbeans.modules.jumpto.settings;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.prefs.Preferences;
-import org.netbeans.api.annotations.common.NonNull;
-import org.netbeans.api.annotations.common.NullAllowed;
-import org.openide.util.NbBundle;
-import org.openide.util.NbPreferences;
-
-/**
- *
- * @author Tomas Zezula
- */
-public final class HighlightingSettings {
-
-    public enum Mode {
-        NONE("none", NbBundle.getMessage(HighlightingSettings.class, "NAME_NONE")),
-        ACTIVE("active", NbBundle.getMessage(HighlightingSettings.class, "NAME_ACTIVE")),
-        ALL("all", NbBundle.getMessage(HighlightingSettings.class, "NAME_ALL"));
-
-        private static final Map<String, Mode> modesBySystemName;
-        static {
-            final Map<String,Mode> map = new HashMap<>();
-            for (Mode m : Mode.values()) {
-                map.put(m.getSystemName(), m);
-            }
-            modesBySystemName = Collections.unmodifiableMap(map);
-        }
-
-        private final String systemName;
-        private final String displayName;
-
-        private Mode(@NonNull String systemName, @NonNull String displayName) {
-            assert systemName != null;
-            assert displayName != null;
-            this.systemName = systemName;
-            this.displayName = displayName;
-        }
-
-        @NonNull
-        public String getDisplayName() {
-            return this.displayName;
-        }
-
-        @NonNull
-        String getSystemName() {
-            return this.systemName;
-        }
-
-        @Override
-        public String toString() {
-            return getDisplayName();
-        }
-
-        @NonNull
-        static Mode forSystemName(@NullAllowed final String sysName) {
-            Mode m = modesBySystemName.get(sysName);
-            if (m == null) {
-                m = getDefault();
-            }
-            return m;
-        }
-
-        @NonNull
-        private static Mode getDefault() {
-            return ACTIVE;
-        }
-    }
-
-    public enum Type {
-        BOLD("bold", NbBundle.getMessage(HighlightingSettings.class, "NAME_BOLD")),
-        BACKGROUND("background", NbBundle.getMessage(HighlightingSettings.class, "NAME_BACKGROUND"));
-
-        private static final Map<String,Type> typesBySystemName;
-        static {
-            Map<String,Type> map = new HashMap<>();
-            for (Type t : Type.values()) {
-                map.put(t.getSystemName(), t);
-            }
-            typesBySystemName = Collections.unmodifiableMap(map);
-        }
-
-        private final String systemName;
-        private final String displayName;
-
-        private Type(@NonNull String systemName, @NonNull String displayName) {
-            assert systemName != null;
-            assert displayName != null;
-            this.systemName = systemName;
-            this.displayName = displayName;
-        }
-
-        @NonNull
-        public String getDisplayName() {
-            return this.displayName;
-        }
-
-        @NonNull
-        String getSystemName() {
-            return this.systemName;
-        }
-
-        @Override
-        public String toString() {
-            return getDisplayName();
-        }
-
-        @NonNull
-        static Type forSystemName(@NullAllowed final String systemName) {
-            Type type = typesBySystemName.get(systemName);
-            if (type == null) {
-                type = getDefault();
-            }
-            return type;
-        }
-
-        @NonNull
-        private static Type getDefault() {
-            return BACKGROUND;
-        }
-    }
-
-    private static final String NODE_NAME = "highlighting";  //NOI18N
-    private static final String KEY_MODE = "mode";      //NOI18N
-    private static final String KEY_TYPE = "type";      //NOI18N
-    private static final HighlightingSettings INSTANCE = new HighlightingSettings();
-
-    private HighlightingSettings() {
-    }
-
-    @NonNull
-    public Mode getMode() {
-        return Mode.forSystemName(getNode().get(KEY_MODE, null));
-    }
-
-    void setMode(@NonNull final Mode mode) {
-        assert mode != null;
-        getNode().put(KEY_MODE, mode.getSystemName());
-    }
-
-    @NonNull
-    public Type getType() {
-        return Type.forSystemName(getNode().get(KEY_TYPE, null));
-    }
-
-    void setType(@NonNull final Type type) {
-        assert type != null;
-        getNode().put(KEY_TYPE, type.getSystemName());
-    }
-
-    @NonNull
-    private Preferences getNode() {
-        final Preferences prefs = NbPreferences.forModule(HighlightingSettings.class);
-        return prefs.node(NODE_NAME);
-    }
-
-    @NonNull
-    public static HighlightingSettings getDefault() {
-        return INSTANCE;
-    }
-}
diff --git a/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.form b/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.form
index 097fcee04..49e39da57 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.form
+++ b/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.form
@@ -45,21 +45,45 @@
     <DimensionLayout dim="0">
       <Group type="103" groupAlignment="0" attributes="0">
           <Group type="102" alignment="0" attributes="0">
-              <EmptySpace min="6" pref="6" max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Component id="jLabel3" alignment="0" min="-2" max="-2" attributes="0"/>
-                  <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
-              </Group>
+              <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="0" max="-2" attributes="0">
-                  <Component id="highlight" max="32767" attributes="0"/>
-                  <Component id="by" min="-2" pref="200" max="-2" attributes="0"/>
+              <Component id="jSeparator1" max="32767" attributes="0"/>
+          </Group>
+          <Group type="102" attributes="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="103" groupAlignment="1" attributes="0">
+                      <Group type="102" attributes="0">
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="orderByLabel" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="51" max="-2" attributes="0"/>
+                          <Component id="orderBy" min="-2" pref="200" max="-2" attributes="0"/>
+                      </Group>
+                      <Group type="102" attributes="0">
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Group type="102" alignment="0" attributes="0">
+                                  <EmptySpace min="6" pref="6" max="-2" attributes="0"/>
+                                  <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+                              </Group>
+                              <Group type="102" alignment="0" attributes="0">
+                                  <EmptySpace max="-2" attributes="0"/>
+                                  <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
+                              </Group>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="0" max="-2" attributes="0">
+                              <Component id="highlight" max="32767" attributes="0"/>
+                              <Component id="by" min="-2" pref="200" max="-2" attributes="0"/>
+                          </Group>
+                      </Group>
+                  </Group>
+                  <Component id="jLabel4" alignment="0" min="-2" max="-2" attributes="0"/>
               </Group>
+              <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
           </Group>
           <Group type="102" alignment="0" attributes="0">
-              <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
-              <Component id="jSeparator1" max="32767" attributes="0"/>
+              <Component id="preferOpenPrj" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="32767" attributes="0"/>
           </Group>
       </Group>
     </DimensionLayout>
@@ -80,7 +104,16 @@
                   <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
                   <Component id="by" alignment="3" min="-2" max="-2" attributes="0"/>
               </Group>
-              <EmptySpace pref="88" max="32767" attributes="0"/>
+              <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
+              <Component id="jLabel4" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="orderBy" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="orderByLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="preferOpenPrj" min="-2" max="-2" attributes="0"/>
+              <EmptySpace pref="37" max="32767" attributes="0"/>
           </Group>
       </Group>
     </DimensionLayout>
@@ -99,7 +132,7 @@
     <Component class="javax.swing.JComboBox" name="highlight">
       <Properties>
         <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-          <Connection code="new DefaultComboBoxModel(HighlightingSettings.Mode.values())" type="code"/>
+          <Connection code="new DefaultComboBoxModel(org.netbeans.modules.jumpto.settings.GoToSettings.HighlightingMode.values())" type="code"/>
         </Property>
       </Properties>
       <AccessibilityProperties>
@@ -131,7 +164,7 @@
     <Component class="javax.swing.JComboBox" name="by">
       <Properties>
         <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-          <Connection code="new DefaultComboBoxModel(HighlightingSettings.Type.values())" type="code"/>
+          <Connection code="new DefaultComboBoxModel(org.netbeans.modules.jumpto.settings.GoToSettings.HighlightingType.values())" type="code"/>
         </Property>
       </Properties>
       <AccessibilityProperties>
@@ -142,5 +175,34 @@
     </Component>
     <Component class="javax.swing.JSeparator" name="jSeparator1">
     </Component>
+    <Component class="javax.swing.JLabel" name="orderByLabel">
+      <Properties>
+        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+          <ComponentRef name="orderBy"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/jumpto/settings/Bundle.properties" key="JumpToPanel.orderByLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="orderBy">
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;GoToSettings.SortingType&gt;"/>
+      </AuxValues>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel4">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/jumpto/settings/Bundle.properties" key="JumpToPanel.jLabel4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JCheckBox" name="preferOpenPrj">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/jumpto/settings/Bundle.properties" key="JumpToPanel.preferOpenPrj.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
   </SubComponents>
 </Form>
diff --git a/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.java b/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.java
index 44dba64e8..90a59b9fc 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/settings/JumpToPanel.java
@@ -18,9 +18,12 @@
  */
 package org.netbeans.modules.jumpto.settings;
 
+import java.awt.Component;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
 import org.netbeans.api.options.OptionsDisplayer;
 import org.netbeans.spi.options.OptionsPanelController;
 
@@ -34,8 +37,14 @@
     JumpToPanel(JumpToOptionsPanelController controller) {
         this.controller = controller;
         initComponents();
+        for (GoToSettings.SortingType t : GoToSettings.SortingType.values()) {
+            orderBy.addItem(t);
+        }
+        orderBy.setRenderer(new SortingTypeRenderer());
         highlight.addActionListener(this);
         by.addActionListener(this);
+        orderBy.addActionListener(this);
+        preferOpenPrj.addActionListener(this);
     }
 
     /**
@@ -52,13 +61,17 @@ private void initComponents() {
         jLabel3 = new javax.swing.JLabel();
         by = new javax.swing.JComboBox();
         jSeparator1 = new javax.swing.JSeparator();
+        orderByLabel = new javax.swing.JLabel();
+        orderBy = new javax.swing.JComboBox<>();
+        jLabel4 = new javax.swing.JLabel();
+        preferOpenPrj = new javax.swing.JCheckBox();
 
         setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
 
         jLabel1.setLabelFor(highlight);
         org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(JumpToPanel.class, "LBL_Highlight")); // NOI18N
 
-        highlight.setModel(new DefaultComboBoxModel(HighlightingSettings.Mode.values()));
+        highlight.setModel(new DefaultComboBoxModel(org.netbeans.modules.jumpto.settings.GoToSettings.HighlightingMode.values()));
 
         org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(JumpToPanel.class, "LBL_Highlighting")); // NOI18N
 
@@ -66,25 +79,49 @@ private void initComponents() {
         org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(JumpToPanel.class, "LBL_Highlight_By")); // NOI18N
         jLabel3.setToolTipText(org.openide.util.NbBundle.getMessage(JumpToPanel.class, "JumpToPanel.jLabel3.toolTipText")); // NOI18N
 
-        by.setModel(new DefaultComboBoxModel(HighlightingSettings.Type.values()));
+        by.setModel(new DefaultComboBoxModel(org.netbeans.modules.jumpto.settings.GoToSettings.HighlightingType.values()));
+
+        orderByLabel.setLabelFor(orderBy);
+        org.openide.awt.Mnemonics.setLocalizedText(orderByLabel, org.openide.util.NbBundle.getMessage(JumpToPanel.class, "JumpToPanel.orderByLabel.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(JumpToPanel.class, "JumpToPanel.jLabel4.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(preferOpenPrj, org.openide.util.NbBundle.getMessage(JumpToPanel.class, "JumpToPanel.preferOpenPrj.text")); // NOI18N
 
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
         this.setLayout(layout);
         layout.setHorizontalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGroup(layout.createSequentialGroup()
-                .addGap(6, 6, 6)
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addComponent(jLabel3)
-                    .addComponent(jLabel1))
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
-                    .addComponent(highlight, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                    .addComponent(by, javax.swing.GroupLayout.PREFERRED_SIZE, 200, javax.swing.GroupLayout.PREFERRED_SIZE)))
             .addGroup(layout.createSequentialGroup()
                 .addComponent(jLabel2)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jSeparator1))
+            .addGroup(layout.createSequentialGroup()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                        .addGroup(layout.createSequentialGroup()
+                            .addContainerGap()
+                            .addComponent(orderByLabel)
+                            .addGap(51, 51, 51)
+                            .addComponent(orderBy, javax.swing.GroupLayout.PREFERRED_SIZE, 200, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addGroup(layout.createSequentialGroup()
+                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                .addGroup(layout.createSequentialGroup()
+                                    .addGap(6, 6, 6)
+                                    .addComponent(jLabel3))
+                                .addGroup(layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .addComponent(jLabel1)))
+                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+                                .addComponent(highlight, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(by, javax.swing.GroupLayout.PREFERRED_SIZE, 200, javax.swing.GroupLayout.PREFERRED_SIZE))))
+                    .addComponent(jLabel4))
+                .addGap(0, 0, Short.MAX_VALUE))
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(preferOpenPrj)
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
         layout.setVerticalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -100,7 +137,15 @@ private void initComponents() {
                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(jLabel3)
                     .addComponent(by, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addContainerGap(88, Short.MAX_VALUE))
+                .addGap(18, 18, 18)
+                .addComponent(jLabel4)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(orderBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(orderByLabel))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(preferOpenPrj)
+                .addContainerGap(37, Short.MAX_VALUE))
         );
 
         highlight.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(JumpToPanel.class, "AD_Highlight")); // NOI18N
@@ -108,15 +153,19 @@ private void initComponents() {
     }// </editor-fold>//GEN-END:initComponents
 
     void load() {
-        final HighlightingSettings settings = HighlightingSettings.getDefault();
-        highlight.setSelectedItem(settings.getMode());
-        by.setSelectedItem(settings.getType());
+        final GoToSettings settings = GoToSettings.getDefault();
+        highlight.setSelectedItem(settings.getHighlightingMode());
+        by.setSelectedItem(settings.getHighlightingType());
+        orderBy.setSelectedItem(settings.getSortingType());
+        preferOpenPrj.setSelected(settings.isSortingPreferOpenProjects());
     }
 
     void store() {
-        final HighlightingSettings settings = HighlightingSettings.getDefault();
-        settings.setMode((HighlightingSettings.Mode)highlight.getSelectedItem());
-        settings.setType((HighlightingSettings.Type)by.getSelectedItem());
+        final GoToSettings settings = GoToSettings.getDefault();
+        settings.setHighlightingMode((GoToSettings.HighlightingMode)highlight.getSelectedItem());
+        settings.setHighlightingType((GoToSettings.HighlightingType)by.getSelectedItem());
+        settings.setSortingType((GoToSettings.SortingType)orderBy.getSelectedItem());
+        settings.setSortingPreferOpenProjects(preferOpenPrj.isSelected());
     }
 
     boolean valid() {
@@ -129,17 +178,34 @@ boolean valid() {
     private javax.swing.JLabel jLabel1;
     private javax.swing.JLabel jLabel2;
     private javax.swing.JLabel jLabel3;
+    private javax.swing.JLabel jLabel4;
     private javax.swing.JSeparator jSeparator1;
+    private javax.swing.JComboBox<GoToSettings.SortingType> orderBy;
+    private javax.swing.JLabel orderByLabel;
+    private javax.swing.JCheckBox preferOpenPrj;
     // End of variables declaration//GEN-END:variables
 
     @Override
     public void actionPerformed(ActionEvent e) {
         boolean isChanged = false;
-        HighlightingSettings settings = HighlightingSettings.getDefault();
-        if ((HighlightingSettings.Mode)highlight.getSelectedItem() != settings.getMode()
-                || (HighlightingSettings.Type)by.getSelectedItem() != settings.getType()) {
+        GoToSettings settings = GoToSettings.getDefault();
+        if (highlight.getSelectedItem() != settings.getHighlightingMode()
+                || by.getSelectedItem() != settings.getHighlightingType()
+                || orderBy.getSelectedItem() != settings.getSortingType()
+                || preferOpenPrj.isSelected() ^ settings.isSortingPreferOpenProjects()) {
             isChanged = true;
         }
         controller.changed(isChanged);
     }
+
+    private static final class SortingTypeRenderer extends DefaultListCellRenderer {
+
+        @Override
+        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            if (value instanceof GoToSettings.SortingType) {
+                value = ((GoToSettings.SortingType)value).getDisplayName();
+            }
+            return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+        }
+    }
 }
diff --git a/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java b/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java
index df7790042..2e9cf82c6 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java
@@ -45,6 +45,7 @@
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.annotations.common.CheckForNull;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.jumpto.EntityComparator;
 import org.netbeans.modules.jumpto.common.AbstractModelFilter;
 import org.netbeans.modules.jumpto.common.CurrentSearch;
 import org.netbeans.modules.jumpto.common.Factory;
@@ -52,6 +53,7 @@
 import org.netbeans.modules.jumpto.common.Models;
 import org.netbeans.modules.jumpto.common.Models.MutableListModel;
 import org.netbeans.modules.jumpto.common.Utils;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.spi.jumpto.support.AsyncDescriptor;
 import org.netbeans.spi.jumpto.symbol.SymbolDescriptor;
 import org.netbeans.spi.jumpto.symbol.SymbolProvider;
@@ -125,6 +127,7 @@ protected void update(@NonNull final SymbolDescriptor item) {
     private Worker running;
     //threading: accessed only in EDT
     private Dialog dialog;
+    private volatile SymbolComparator itemsComparator;
 
 
     public ContentProviderImpl(final JButton okButton) {
@@ -204,11 +207,12 @@ public boolean setListModel(GoToPanel panel, String text) {
             final SymbolDescriptorAttrCopier acp = currentSearch.getAttribute(SymbolDescriptorAttrCopier.class);
             final boolean correctCase = acp == null || acp.hasCorrectCase();
             if (currentSearch.isNarrowing(searchType, name, scope, correctCase)) {
+                itemsComparator.setText(name);
                 currentSearch.filter(searchType, name, null);
                 enableOK(panel.revalidateModel(true));
                 return false;
             } else {
-                running = new Worker(text, searchType, panel);
+                running = new Worker(text, name, searchType, panel);
                 task = rp.post( running, 500);
                 if ( panel.getStartTime() != -1 ) {
                     LOG.log(
@@ -244,7 +248,7 @@ Runnable createWorker(
             @NonNull final String text,
             @NonNull final SearchType searchType,
             @NonNull final GoToPanel panel) {
-        return new Worker(text, searchType, panel);
+        return new Worker(text, text, searchType, panel);
     }
 
     private void enableOK(final boolean enabled) {
@@ -319,6 +323,7 @@ public boolean isFromCurrentProject(@NonNull final SymbolDescriptor item) {
     private class Worker implements Runnable {
 
         private final String text;
+        private final String name;
         private final SearchType searchType;
         private final long createTime;
         private final GoToPanel panel;
@@ -328,9 +333,11 @@ public boolean isFromCurrentProject(@NonNull final SymbolDescriptor item) {
 
         Worker(
                 @NonNull final String text,
+                @NonNull final String name,
                 @NonNull final SearchType searchType,
                 @NonNull final GoToPanel panel ) {
             this.text = text;
+            this.name = name;
             this.searchType = searchType;
             this.panel = panel;
             this.createTime = System.currentTimeMillis();
@@ -351,8 +358,14 @@ public void run() {
             int lastSize = -1, lastProvCount = providers.size();
             final int[] newSize = new int[1];
             final SymbolDescriptorAttrCopier attrCopier = new SymbolDescriptorAttrCopier();
+            final SymbolComparator ic = SymbolComparator.create(
+                    GoToSettings.getDefault().getSortingType(),
+                    name,
+                    Utils.isCaseSensitive(searchType),
+                    GoToSettings.getDefault().isSortingPreferOpenProjects());
+            itemsComparator = ic;
             final MutableListModel<SymbolDescriptor> model = Models.mutable(
-                    new SymbolComparator(),
+                    ic,
                     currentSearch.resetFilter(),
                     attrCopier);
             try {
diff --git a/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java b/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java
index abfc84021..d6b40f9ae 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java
@@ -19,9 +19,15 @@
 
 package org.netbeans.modules.jumpto.symbol;
 
+import java.util.Objects;
+import javax.swing.event.ChangeListener;
 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.modules.jumpto.EntityComparator;
+import org.netbeans.modules.jumpto.common.DescriptorAccessor;
+import org.netbeans.modules.jumpto.common.StateFullComparator;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.spi.jumpto.symbol.SymbolDescriptor;
+import org.openide.util.ChangeSupport;
 
 /**
  * The {@code SymbolComparator} establishes the sort order of the symbols.
@@ -33,46 +39,198 @@
  *
  * @author Victor G. Vasilyev <vv...@netbeans.org>
  */
-public class SymbolComparator extends EntityComparator<SymbolDescriptor> {
-
-    /**
-     * Compares its two {@code SymbolDescriptor}s for order.
-     * <p>
-     * This method establishes the following groups for order
-     * (from lowest to highest):
-     * <ul>
-     * <li>Symbols being defined in the main project (if any)</li>
-     * <li>Symbols being defined in the projects that are opened in the
-     *     IDE's GUI</li>
-     * <li>Symbols being defined in other accessible projects.</li>
-     * </ul>
-     * </p>
-     * The alphabetical order of the symbol names is established inside each
-     * group.<br/>
-     * If the symbols names are the same then the alphabetical order of
-     * the owner names of the symbols is used.<br/>
-     *
-     * @param e1 the first {@code SymbolDescriptor} to be compared.
-     * @param e2 the second {@code SymbolDescriptor} to be compared.
-     * @return a negative integer, zero, or a positive integer as the
-     * 	   first argument is less than, equal to, or greater than the
-     *	   second.
-     */
+public abstract class SymbolComparator extends EntityComparator<SymbolDescriptor> implements StateFullComparator<SymbolDescriptor> {
+
+    private final ChangeSupport support;
+    protected final boolean caseSensitive;
+    protected final boolean preferOpPrjs;
+
+    private SymbolComparator(
+            final boolean caseSensitive,
+            final boolean preferOpPrjs) {
+        this.caseSensitive = caseSensitive;
+        this.preferOpPrjs = preferOpPrjs;
+        this.support = new ChangeSupport(this);
+    }
+
+    @Override
+    public void addChangeListener(@NonNull final ChangeListener listener) {
+        support.addChangeListener(listener);
+    }
+
+    @Override
+    public void removeChangeListener(@NonNull final ChangeListener listener) {
+        support.removeChangeListener(listener);
+    }
+
+    void fireChange() {
+        support.fireChange();
+    }
+
+    abstract void setText(@NonNull final String text);
+
     @Override
-    public int compare(SymbolDescriptor e1, SymbolDescriptor e2) {
-        String e1projectName = e1.getProjectName();
-        String e2projectName = e2.getProjectName();
-        int result = compareProjects(e1projectName, e2projectName);
-        if(result != 0) {
-            return result; // e1projectName NOT equals to e2projectName
+    public abstract int compare(SymbolDescriptor e1, SymbolDescriptor e2);
+
+
+    private static final class Alphabet extends SymbolComparator {
+
+        Alphabet(
+                final boolean caseSensitive,
+                final boolean preferOpPrjs) {
+            super(caseSensitive, preferOpPrjs);
+        }
+        /**
+         * Compares its two {@code SymbolDescriptor}s for order.
+         * <p>
+         * This method establishes the following groups for order
+         * (from lowest to highest):
+         * <ul>
+         * <li>Symbols being defined in the main project (if any)</li>
+         * <li>Symbols being defined in the projects that are opened in the
+         *     IDE's GUI</li>
+         * <li>Symbols being defined in other accessible projects.</li>
+         * </ul>
+         * </p>
+         * The alphabetical order of the symbol names is established inside each
+         * group.<br/>
+         * If the symbols names are the same then the alphabetical order of
+         * the owner names of the symbols is used.<br/>
+         *
+         * @param e1 the first {@code SymbolDescriptor} to be compared.
+         * @param e2 the second {@code SymbolDescriptor} to be compared.
+         * @return a negative integer, zero, or a positive integer as the
+         * 	   first argument is less than, equal to, or greater than the
+         *	   second.
+         */
+        @Override
+        public int compare(SymbolDescriptor e1, SymbolDescriptor e2) {
+            int result;
+            if (preferOpPrjs) {
+                String e1projectName = e1.getProjectName();
+                String e2projectName = e2.getProjectName();
+                result = compareProjects(e1projectName, e2projectName);
+                if(result != 0) {
+                    return result; // e1projectName NOT equals to e2projectName
+                }
+            }
+            // here: e1projectName equals to e2projectName
+            result = compare(getSortName(e1), getSortName(e2), caseSensitive);
+            if ( result != 0 ) {
+               return result;
+            }
+            // here: e1Name equals to e2Name
+            return compare(e1.getOwnerName(), e2.getOwnerName(), caseSensitive);
+        }
+
+        @Override
+        void setText(String text) {
+        }
+    }
+
+    private static final class Levenshtein extends SymbolComparator {
+
+        private static final String ATTR_PATTERN = "Pattern";       //NOI18N
+        private static final String ATTR_LS_DIST = "LevenshteinDistance";   //NOI18N
+        private static final String ATTR_LS_TAIL = "LevenshteinTail";   //NOI18N
+
+
+        private String text;
+
+        Levenshtein(
+                @NonNull final String text,
+                final boolean caseSensitive,
+                final boolean preferOpPrjs) {
+            super(caseSensitive, preferOpPrjs);
+            this.text = text;
+        }
+
+        @Override
+        public int compare(SymbolDescriptor e1, SymbolDescriptor e2) {
+            int result;
+            if (preferOpPrjs) {
+                String e1projectName = e1.getProjectName();
+                String e2projectName = e2.getProjectName();
+                result = compareProjects(e1projectName, e2projectName);
+                if(result != 0) {
+                    return result; // e1projectName NOT equals to e2projectName
+                }
+            }
+            final String name1 = getSortName(e1);
+            final String name2 = getSortName(e2);
+
+            int l1, l2, t1, t2;
+            Object d = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_LS_DIST);
+            Object t = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_LS_TAIL);
+            Object p = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_PATTERN);
+            if (d instanceof Integer && t instanceof Integer && text.equals(p)) {
+                l1 = (Integer) d;
+                t1 = (Integer) t;
+            } else {
+                final String prefix = levenshteinPrefix(name1, text, caseSensitive);
+                l1 = levenshteinDistance(prefix, text, caseSensitive);
+                t1 = name1.length() - prefix.length();
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_LS_DIST, l1);
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_LS_TAIL, t1);
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_PATTERN, text);
+            }
+            d = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_LS_DIST);
+            t = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_LS_TAIL);
+            p = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_PATTERN);
+            if (d instanceof Integer && t instanceof Integer && text.equals(p)) {
+                l2 = (Integer) d;
+                t2 = (Integer) t;
+            } else {
+                final String prefix = levenshteinPrefix(name2, text, caseSensitive);
+                l2 = levenshteinDistance(prefix, text, caseSensitive);
+                t2 = name2.length() - prefix.length();
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_LS_DIST, l2);
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_LS_TAIL, t2);
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_PATTERN, text);
+            }
+
+            result = l1 - l2;
+            if (result != 0) {
+                return result;
+            }
+            result = t1 - t2;
+            if (result != 0) {
+                return result;
+            }
+
+            // here: e1projectName equals to e2projectName
+            result = compare(name1, name2, caseSensitive);
+            if ( result != 0 ) {
+               return result;
+            }
+            // here: e1Name equals to e2Name
+            return compare(e1.getOwnerName(), e2.getOwnerName(), caseSensitive);
         }
-        // here: e1projectName equals to e2projectName
-        result = compare(getSortName(e1), getSortName(e2));
-        if ( result != 0 ) {
-           return result;
+
+        @Override
+        void setText(String text) {
+            final boolean fire = !Objects.equals(this.text, text);
+            this.text = text;
+            if (fire) {
+                fireChange();
+            }
+        }
+    }
+
+    @NonNull
+    public static SymbolComparator create(
+            @NonNull final GoToSettings.SortingType kind,
+            @NonNull final String text,
+            final boolean caseSensitive,
+            final boolean preferOpPrjs) {
+        switch (kind) {
+            case LEXICOGRAPHIC:
+                return new Alphabet(caseSensitive, preferOpPrjs);
+            case LEVENSHTEIN:
+                return new Levenshtein(text, caseSensitive, preferOpPrjs);
+            default:
+                throw new IllegalArgumentException(String.valueOf(kind));
         }
-        // here: e1Name equals to e2Name
-        return compare(e1.getOwnerName(), e2.getOwnerName());
     }
 
     @NonNull
diff --git a/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java b/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java
index b74feeac8..1c983d83e 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java
@@ -62,10 +62,12 @@
 import org.netbeans.api.jumpto.type.TypeBrowser;
 import org.netbeans.api.project.ui.OpenProjects;
 import org.netbeans.editor.JumpList;
+import org.netbeans.modules.jumpto.EntityComparator;
 import org.netbeans.modules.jumpto.common.AbstractModelFilter;
 import org.netbeans.modules.jumpto.common.CurrentSearch;
 import org.netbeans.modules.jumpto.common.ItemRenderer;
 import org.netbeans.modules.jumpto.common.Utils;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.modules.sampler.Sampler;
 import org.openide.DialogDescriptor;
 import org.openide.DialogDisplayer;
@@ -107,6 +109,7 @@
     private final String title;
     private final boolean multiSelection;
     private final CurrentSearch<TypeDescriptor> currentSearch;
+    private volatile TypeComparator itemsComparator;
 
     /** Creates a new instance of OpenTypeAction */
     public GoToTypeAction() {
@@ -277,11 +280,12 @@ public void mouseClicked(java.awt.event.MouseEvent evt) {
 
         // Compute in other thread
         if (currentSearch.isNarrowing(nameKind, name, scope, true)) {
+            itemsComparator.setText(name);
             currentSearch.filter(nameKind, name, null);
             enableOK(panel.revalidateModel());
             return false;
         } else {
-            running = new Worker(text, panel.isCaseSensitive());
+            running = new Worker(text, name, panel.isCaseSensitive());
             task = rp.post( running, 500);
             if ( panel.time != -1 ) {
                 LOGGER.log( Level.FINE, "Worker posted after {0} ms.", System.currentTimeMillis() - panel.time ); //NOI18N
@@ -454,11 +458,16 @@ public boolean isFromCurrentProject(@NonNull final TypeDescriptor item) {
         private volatile boolean isCanceled = false;
         private volatile TypeProvider current;
         private final String text;
+        private final String name;
         private final boolean caseSensitive;
         private final long createTime;
 
-        public Worker( String text, final boolean caseSensitive) {
+        public Worker(
+                String text,
+                String name,
+                final boolean caseSensitive) {
             this.text = text;
+            this.name = name;
             this.caseSensitive = caseSensitive;
             this.createTime = System.currentTimeMillis();
             LOGGER.log( Level.FINE, "Worker for {0} - created after {1} ms.",   //NOI18N
@@ -486,6 +495,19 @@ public void run() {
                 }
             }
             int lastSize = -1;
+            final TypeComparator ic = TypeComparator.create(
+                    GoToSettings.getDefault().getSortingType(),
+                    name,
+                    caseSensitive,
+                    GoToSettings.getDefault().isSortingPreferOpenProjects());
+            itemsComparator = ic;
+            final Models.MutableListModel baseModel = Models.mutable(
+                ic,
+                currentSearch.resetFilter(),
+                null);
+            final ListModel model = typeFilter != null ?
+                    FilteredListModel.create(baseModel, new FilterAdaptor(typeFilter), NbBundle.getMessage(GoToTypeAction.class, "LBL_Computing")) :
+                    baseModel;
             for (;;) {
                 final int[] retry = new int[1];
 
@@ -513,15 +535,9 @@ public void run() {
                     //Unfortunatelly no TypeDescriptor impl provides equals.
                     if (resultChanged || done) {
                         lastSize = newSize;
-                        final ListModel fmodel;
                         if (resultChanged) {
-                            ListModel model = Models.fromList(types, currentSearch.resetFilter(), null);
-                            if (typeFilter != null) {
-                                model = FilteredListModel.create(model, new FilterAdaptor(typeFilter), NbBundle.getMessage(GoToTypeAction.class, "LBL_Computing"));
-                            }
-                            fmodel = model;
-                        } else {
-                            fmodel = null;
+                            baseModel.clear();
+                            baseModel.add(types);
                         }
                         if (isCanceled) {
                             return;
@@ -531,8 +547,8 @@ public void run() {
                                 final Pair<String, String> nameAndScope = Utils.splitNameAndScope(text);
                                 currentSearch.searchCompleted(nameKind, nameAndScope.first(), nameAndScope.second());
                             }
-                            if (fmodel != null && !isCanceled) {
-                                enableOK(panel.setModel(fmodel));
+                            if (resultChanged && !isCanceled) {
+                                enableOK(panel.setModel(model));
                             }
                         });
                     }
@@ -577,7 +593,7 @@ public void cancel() {
         }
 
         @SuppressWarnings("unchecked")
-        private List<? extends TypeDescriptor> getTypeNames(String text, int[] retry) {
+        private List<? extends TypeDescriptor> getTypeNames(final String text, int[] retry) {
             // TODO: Search twice, first for current project, then for all projects
             final Set<TypeDescriptor> items = new HashSet<>();
             final String[] message = new String[1];
@@ -609,11 +625,9 @@ public void cancel() {
             }
             if ( !isCanceled ) {
                 final ArrayList<TypeDescriptor> result = new ArrayList<>(items);
-                Collections.sort(result, new TypeComparator(caseSensitive));
                 panel.setWarning(message[0]);
                 return result;
-            }
-            else {
+            } else {
                 return null;
             }
         }
diff --git a/jumpto/src/org/netbeans/modules/jumpto/type/TypeComparator.java b/jumpto/src/org/netbeans/modules/jumpto/type/TypeComparator.java
index e024390f6..30f01ca02 100644
--- a/jumpto/src/org/netbeans/modules/jumpto/type/TypeComparator.java
+++ b/jumpto/src/org/netbeans/modules/jumpto/type/TypeComparator.java
@@ -19,8 +19,15 @@
 
 package org.netbeans.modules.jumpto.type;
 
+import java.util.Objects;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.modules.jumpto.EntityComparator;
+import org.netbeans.modules.jumpto.common.DescriptorAccessor;
+import org.netbeans.modules.jumpto.common.StateFullComparator;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
 import org.netbeans.spi.jumpto.type.TypeDescriptor;
+import org.openide.util.ChangeSupport;
 
 /**
  * The {@code TypeComparator} establishes the sort order of the types.
@@ -32,63 +39,205 @@
  *
  * @author Victor G. Vasilyev <vv...@netbeans.org>
  */
-public class TypeComparator extends EntityComparator<TypeDescriptor> {
+public abstract class TypeComparator extends EntityComparator<TypeDescriptor> implements StateFullComparator<TypeDescriptor> {
 
-    private final boolean caseSensitive;
+    private final ChangeSupport support;
+    protected final boolean caseSensitive;
+    protected final boolean preferOpPrjs;
 
-    public TypeComparator(final boolean caseSensitive) {
+    private TypeComparator(
+            final boolean caseSensitive,
+            final boolean preferOpPrjs) {
         this.caseSensitive = caseSensitive;
+        this.preferOpPrjs = preferOpPrjs;
+        this.support = new ChangeSupport(this);
     }
 
-    public TypeComparator() {
-        this(true);
+    @Override
+    public void addChangeListener(@NonNull final ChangeListener listener) {
+        support.addChangeListener(listener);
     }
 
-    /**
-     * Compares its two {@code TypeDescriptor}s for order.
-     * <p>
-     * This method establishes the following groups for order
-     * (from lowest to highest):
-     * <ul>
-     * <li>Types being defined in the main project (if any)</li>
-     * <li>Types being defined in the projects that are opened in the
-     *     IDE's GUI</li>
-     * <li>Types being defined in other accessible projects.</li>
-     * </ul>
-     * </p>
-     * The alphabetical order of the type names is established inside each 
-     * group.<br/>
-     * If the type names are the same then the alphabetical order of
-     * the outer names of the types is used.<br/>
-     * If the outer names are the same then alphabetical order of 
-     * the context names of the types is used.
-     *
-     * @param e1 the first {@code TypeDescriptor} to be compared.
-     * @param e2 the second {@code TypeDescriptor} to be compared.
-     * @return a negative integer, zero, or a positive integer as the
-     * 	   first argument is less than, equal to, or greater than the
-     *	   second.
-     */
     @Override
-    public int compare(TypeDescriptor e1, TypeDescriptor e2) {
-        String e1projectName = e1.getProjectName();
-        String e2projectName = e2.getProjectName();
-        int result = compareProjects(e1projectName, e2projectName);
-        if(result != 0) {
-            return result; // e1projectName NOT equals to e2projectName
+    public void removeChangeListener(@NonNull final ChangeListener listener) {
+        support.removeChangeListener(listener);
+    }
+
+    public abstract int compare(TypeDescriptor e1, TypeDescriptor e2);
+
+    abstract void setText(@NonNull final String text);
+
+    protected void fireChange() {
+        support.fireChange();
+    }
+
+    private static final class Levenshtein extends TypeComparator {
+
+        private static final String ATTR_PATTERN = "Pattern";       //NOI18N
+        private static final String ATTR_LS_DIST = "LevenshteinDistance";   //NOI18N
+        private static final String ATTR_LS_TAIL = "LevenshteinTail";   //NOI18N
+
+        protected String text;
+
+        Levenshtein(
+                @NonNull final String text,
+                final boolean caseSensitive,
+                final boolean preferOpPrjs) {
+            super(caseSensitive, preferOpPrjs);
+            this.text = text;
         }
-        // here: e1projectName equals to e2projectName
-        result = compare(e1.getTypeName(), e2.getTypeName(), caseSensitive);
-        if ( result != 0 ) {
-           return result;
+
+        @Override
+        void setText(@NonNull final String text) {
+            final boolean fire = !Objects.equals(this.text, text);
+            this.text = text;
+            if (fire) {
+                fireChange();
+            }
         }
-        // here: e1Name equals to e2Name
-        result = compare(e1.getOuterName(), e2.getOuterName());
-        if ( result != 0 ) {
-           return result;
+
+        @Override
+        public int compare(TypeDescriptor e1, TypeDescriptor e2) {
+            int result;
+            if (preferOpPrjs) {
+                String e1projectName = e1.getProjectName();
+                String e2projectName = e2.getProjectName();
+                result = compareProjects(e1projectName, e2projectName);
+                if(result != 0) {
+                    return result; // e1projectName NOT equals to e2projectName
+                }
+            }
+            int l1, l2, t1, t2;
+            Object o = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_LS_DIST);
+            Object t = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_LS_TAIL);
+            Object p = DescriptorAccessor.getInstance().getAttribute(e1, ATTR_PATTERN);
+            if (o instanceof Integer && t instanceof Integer && text.equals(p)) {
+                l1 = (Integer) o;
+                t1 = (Integer) t;
+            } else {
+                final String tn = e1.getSimpleName();
+                final String prefix = levenshteinPrefix(tn,text, caseSensitive);
+                l1 = levenshteinDistance(prefix, text, caseSensitive);
+                t1 = tn.length() - prefix.length();
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_LS_DIST, l1);
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_LS_TAIL, t1);
+                DescriptorAccessor.getInstance().setAttribute(e1, ATTR_PATTERN, text);
+            }
+            o = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_LS_DIST);
+            t = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_LS_TAIL);
+            p = DescriptorAccessor.getInstance().getAttribute(e2, ATTR_PATTERN);
+            if (o instanceof Integer && t instanceof Integer && text.equals(p)) {
+                l2 = (Integer) o;
+                t2 = (Integer) t;
+            } else {
+                final String tn = e2.getSimpleName();
+                final String prefix = levenshteinPrefix(tn, text, caseSensitive);
+                l2 = levenshteinDistance(prefix, text, caseSensitive);
+                t2 = tn.length() - prefix.length();
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_LS_DIST, l2);
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_LS_TAIL, t2);
+                DescriptorAccessor.getInstance().setAttribute(e2, ATTR_PATTERN, text);
+            }
+
+            result = l1 - l2;
+            if (result != 0) {
+                return result;
+            }
+            result = t1 - t2;
+            if (result != 0) {
+                return result;
+            }
+
+            // here: e1projectName equals to e2projectName
+            result = compare(e1.getTypeName(), e2.getTypeName(), caseSensitive);
+            if ( result != 0 ) {
+               return result;
+            }
+            // here: e1Name equals to e2Name
+            result = compare(e1.getOuterName(), e2.getOuterName());
+            if ( result != 0 ) {
+               return result;
+            }
+            // here: e1OuterName equals to e2OuterName
+            return compare(e1.getContextName(), e2.getContextName());
         }
-        // here: e1OuterName equals to e2OuterName
-        return compare(e1.getContextName(), e2.getContextName());
     }
 
+    private static final class Alphabet extends TypeComparator {
+        Alphabet(
+                final boolean caseSensitive,
+                final boolean preferOpPrjs) {
+            super(caseSensitive, preferOpPrjs);
+        }
+
+        @Override
+        void setText(@NonNull final String text) {
+        }
+
+        /**
+         * Compares its two {@code TypeDescriptor}s for order.
+         * <p>
+         * This method establishes the following groups for order
+         * (from lowest to highest):
+         * <ul>
+         * <li>Types being defined in the main project (if any)</li>
+         * <li>Types being defined in the projects that are opened in the
+         *     IDE's GUI</li>
+         * <li>Types being defined in other accessible projects.</li>
+         * </ul>
+         * </p>
+         * The alphabetical order of the type names is established inside each
+         * group.<br/>
+         * If the type names are the same then the alphabetical order of
+         * the outer names of the types is used.<br/>
+         * If the outer names are the same then alphabetical order of
+         * the context names of the types is used.
+         *
+         * @param e1 the first {@code TypeDescriptor} to be compared.
+         * @param e2 the second {@code TypeDescriptor} to be compared.
+         * @return a negative integer, zero, or a positive integer as the
+         * 	   first argument is less than, equal to, or greater than the
+         *	   second.
+         */
+        @Override
+        public int compare(TypeDescriptor e1, TypeDescriptor e2) {
+            int result;
+            if (preferOpPrjs) {
+                String e1projectName = e1.getProjectName();
+                String e2projectName = e2.getProjectName();
+                result = compareProjects(e1projectName, e2projectName);
+                if(result != 0) {
+                    return result; // e1projectName NOT equals to e2projectName
+                }
+            }
+            // here: e1projectName equals to e2projectName
+            result = compare(e1.getTypeName(), e2.getTypeName(), caseSensitive);
+            if ( result != 0 ) {
+               return result;
+            }
+            // here: e1Name equals to e2Name
+            result = compare(e1.getOuterName(), e2.getOuterName());
+            if ( result != 0 ) {
+               return result;
+            }
+            // here: e1OuterName equals to e2OuterName
+            return compare(e1.getContextName(), e2.getContextName());
+        }
+    }
+
+    @NonNull
+    public static TypeComparator create(
+            @NonNull final GoToSettings.SortingType kind,
+            @NonNull final String text,
+            final boolean caseSensitive,
+            final boolean preferOpPrjs) {
+        switch (kind) {
+            case LEVENSHTEIN:
+                return new Levenshtein(text, caseSensitive, preferOpPrjs);
+            case LEXICOGRAPHIC:
+                return new Alphabet(caseSensitive, preferOpPrjs);
+            default:
+                throw new IllegalArgumentException(String.valueOf(kind));
+        }
+    }
 }
diff --git a/jumpto/src/org/netbeans/spi/jumpto/file/FileDescriptor.java b/jumpto/src/org/netbeans/spi/jumpto/file/FileDescriptor.java
index e1d8f98f4..d0d78fb04 100644
--- a/jumpto/src/org/netbeans/spi/jumpto/file/FileDescriptor.java
+++ b/jumpto/src/org/netbeans/spi/jumpto/file/FileDescriptor.java
@@ -21,6 +21,7 @@
 
 import javax.swing.Icon;
 import org.netbeans.api.project.SourceGroup;
+import org.netbeans.spi.jumpto.support.Descriptor;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 
@@ -30,7 +31,7 @@
  * @author Tomas Zezula
  *
  */
-public abstract class FileDescriptor {
+public abstract class FileDescriptor extends Descriptor {
 
     private boolean preferred;
     private int lineNr = -1;
diff --git a/jumpto/src/org/netbeans/spi/jumpto/support/Descriptor.java b/jumpto/src/org/netbeans/spi/jumpto/support/Descriptor.java
new file mode 100644
index 000000000..e89476710
--- /dev/null
+++ b/jumpto/src/org/netbeans/spi/jumpto/support/Descriptor.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.spi.jumpto.support;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.modules.jumpto.common.DescriptorAccessor;
+import org.openide.util.Parameters;
+
+/**
+ * A base for the descriptor classes.
+ * @author Tomas Zezula
+ * @since 1.55
+ */
+public class Descriptor {
+    static {
+        DescriptorAccessor.setInstance(new DescriptorAccessorImpl());
+    }
+
+    private final Map<String,Object> attrs = new HashMap<>();
+
+    @CheckForNull
+    final Object getAttribute(@NonNull final String attr) {
+        Parameters.notNull("attr", attr);   //NOI18N
+        return attrs.get(attr);
+    }
+
+    final void setAttribute(
+            @NonNull final String attr,
+            @NullAllowed final Object value) {
+        Parameters.notNull("attr", attr);   //NOI18N
+        this.attrs.put(attr, value);
+    }
+
+    private static final class DescriptorAccessorImpl extends DescriptorAccessor {
+
+        @Override
+        public Object getAttribute(
+                @NonNull final Descriptor descriptor,
+                @NonNull final String attribute) {
+            return descriptor.getAttribute(attribute);
+        }
+
+        @Override
+        public void setAttribute(
+                @NonNull final Descriptor descriptor,
+                @NonNull final String attribute,
+                @NullAllowed final Object value) {
+            descriptor.setAttribute(attribute, value);
+        }
+    }
+}
diff --git a/jumpto/src/org/netbeans/spi/jumpto/symbol/SymbolDescriptor.java b/jumpto/src/org/netbeans/spi/jumpto/symbol/SymbolDescriptor.java
index 1ed9651ad..21b4bb55c 100644
--- a/jumpto/src/org/netbeans/spi/jumpto/symbol/SymbolDescriptor.java
+++ b/jumpto/src/org/netbeans/spi/jumpto/symbol/SymbolDescriptor.java
@@ -22,6 +22,7 @@
 import javax.swing.Icon;
 import org.netbeans.api.annotations.common.CheckForNull;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.spi.jumpto.support.Descriptor;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.Parameters;
@@ -31,7 +32,7 @@
  * @since 1.7
  * @author Tomas Zezula
  */
-public abstract class SymbolDescriptor {
+public abstract class SymbolDescriptor extends Descriptor {
 
     private String matchedSymbolName;
     private SymbolProvider provider;
diff --git a/jumpto/src/org/netbeans/spi/jumpto/type/TypeDescriptor.java b/jumpto/src/org/netbeans/spi/jumpto/type/TypeDescriptor.java
index f6787effe..3c766fe11 100644
--- a/jumpto/src/org/netbeans/spi/jumpto/type/TypeDescriptor.java
+++ b/jumpto/src/org/netbeans/spi/jumpto/type/TypeDescriptor.java
@@ -21,6 +21,7 @@
 
 import javax.swing.Icon;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.spi.jumpto.support.Descriptor;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.Parameters;
@@ -32,7 +33,7 @@
  * @author Tor Norbye
  * @author Tomas Zezula
  */
-public abstract class TypeDescriptor {
+public abstract class TypeDescriptor extends Descriptor {
 
     private String highlightText;
 
diff --git a/jumpto/test/unit/src/org/netbeans/modules/jumpto/type/TypeComparatorTest.java b/jumpto/test/unit/src/org/netbeans/modules/jumpto/type/TypeComparatorTest.java
new file mode 100644
index 000000000..1e1a97814
--- /dev/null
+++ b/jumpto/test/unit/src/org/netbeans/modules/jumpto/type/TypeComparatorTest.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.jumpto.type;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import javax.swing.Icon;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.jumpto.settings.GoToSettings;
+import org.netbeans.spi.jumpto.type.TypeDescriptor;
+import org.openide.filesystems.FileObject;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public class TypeComparatorTest extends NbTestCase {
+
+    public TypeComparatorTest(final String name) {
+        super(name);
+    }
+
+    public void testSorting() {
+
+        TypeComparator tc = TypeComparator.create(GoToSettings.SortingType.LEVENSHTEIN, "JTC", false, false);   //NOI18N
+        final TypeDescriptor[] tds = new TypeDescriptor[] {
+            new MockTypeDescriptor("JavaTextComponent"),    //NOI18N
+            new MockTypeDescriptor("JTextComponent"),       //NOI18N
+        };
+        Arrays.sort(tds, tc);
+        assertEquals(
+                Arrays.asList(
+                    "JTextComponent",       //NOI18N
+                    "JavaTextComponent"     //NOI18N
+                ),
+                Arrays.stream(tds)
+                .map(TypeDescriptor::getSimpleName)
+                .collect(Collectors.toList()));
+    }
+
+    private static class MockTypeDescriptor extends TypeDescriptor {
+        private final String name;
+
+        MockTypeDescriptor(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getSimpleName() {
+            return name;
+        }
+
+        @Override
+        public String getOuterName() {
+            return "";
+        }
+
+        @Override
+        public String getTypeName() {
+            return name;
+        }
+
+        @Override
+        public String getContextName() {
+            return "";
+        }
+
+        @Override
+        public Icon getIcon() {
+            return null;
+        }
+
+        @Override
+        public String getProjectName() {
+            return null;
+        }
+
+        @Override
+        public Icon getProjectIcon() {
+            return null;
+        }
+
+        @Override
+        public FileObject getFileObject() {
+            return null;
+        }
+
+        @Override
+        public int getOffset() {
+            return -1;
+        }
+
+        @Override
+        public void open() {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+    }
+}
diff --git a/maven/src/org/netbeans/modules/maven/queries/UnitTestsCompilerOptionsQueryImpl.java b/maven/src/org/netbeans/modules/maven/queries/UnitTestsCompilerOptionsQueryImpl.java
index 141525810..70ff8c1f7 100644
--- a/maven/src/org/netbeans/modules/maven/queries/UnitTestsCompilerOptionsQueryImpl.java
+++ b/maven/src/org/netbeans/modules/maven/queries/UnitTestsCompilerOptionsQueryImpl.java
@@ -346,7 +346,7 @@ private static String getModuleName(@NonNull final FileObject moduleInfo) {
                         return Collections.emptyList();
                     }
                     final List<String> result = Arrays.asList(
-                        String.format("-Xmodule:%s", moduleName),       //NOI18N
+                        String.format("-XD-Xmodule:%s", moduleName),       //NOI18N
                         "--add-reads",                                  //NOI18N
                         String.format("%s=ALL-UNNAMED", moduleName));   //NOI18N
                     return Collections.unmodifiableList(result);
diff --git a/o.apache.tools.ant.module/apichanges.xml b/o.apache.tools.ant.module/apichanges.xml
index c6b26554b..8ad087997 100644
--- a/o.apache.tools.ant.module/apichanges.xml
+++ b/o.apache.tools.ant.module/apichanges.xml
@@ -85,6 +85,39 @@ is the proper place.
 <!-- ACTUAL CHANGES BEGIN HERE: -->
 
     <changes>
+        
+        <change id="userExecution">
+            <api name="general"/>
+            <summary>Added a method to mark an execution as user / non-user into the <code>AntTargetExecutor.Env</code></summary>
+            <version major="3" minor="85"/>
+            <date year="2017" month="5" day="2"/>
+            <author login="tzezula"/>
+            <compatibility addition="yes"/>
+            <description>
+                <p>Added a method <code>AntTargetExecutor.Env.setUserAction</code>to mark an execution as user / non-user.
+                The executions marked as user actions are registered in the UI support
+                <code>org.netbeans.spi.project.ui.support.BuildExecutionSupport</code>. By default the execution is an user action.
+                </p>
+            </description>
+            <class package="org.apache.tools.ant.module.api" name="AntTargetExecutor"/>
+        </change>
+
+        <change id="tabReplacePolicy">
+            <api name="general"/>
+            <summary>Added methods to override the default tab replacement policy into the <code>AntTargetExecutor.Env</code></summary>
+            <version major="3" minor="84"/>
+            <date year="2017" month="4" day="27"/>
+            <author login="tzezula"/>
+            <compatibility addition="yes"/>
+            <description>
+                <p>
+                    When the IDE is set to the automatic close tabs mode the tabs created by the previous
+                    run of the <code>AntTargetExecutor</code> are closed by successive run.
+                    Added a support to override this behavior.
+                </p>
+            </description>
+            <class package="org.apache.tools.ant.module.api" name="AntTargetExecutor"/>
+        </change>
 
         <change id="setConcealedProperties">
             <api name="general"/>
diff --git a/o.apache.tools.ant.module/nbproject/project.properties b/o.apache.tools.ant.module/nbproject/project.properties
index 33a475c54..ea6994242 100644
--- a/o.apache.tools.ant.module/nbproject/project.properties
+++ b/o.apache.tools.ant.module/nbproject/project.properties
@@ -16,8 +16,8 @@
 # under the License.
 
 javac.compilerargs=-Xlint:unchecked
-javac.source=1.7
-spec.version.base=3.83.0
+javac.source=1.8
+spec.version.base=3.85.0
 compile.ant.jar=${ant.core.lib}
 compile.ant-launcher.jar=${ant.core.lib}/../ant-launcher.jar
 src-bridge.cp.extra=build/classes:${compile.ant.jar}:${compile.ant-launcher.jar}
diff --git a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java
index c987445cd..5593cd5d2 100644
--- a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java
+++ b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java
@@ -25,9 +25,11 @@
 import java.util.HashSet;
 import java.util.Properties;
 import java.util.Set;
+import java.util.function.Predicate;
 import org.apache.tools.ant.module.AntSettings;
 import org.apache.tools.ant.module.run.TargetExecutor;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.openide.execution.ExecutorTask;
 import org.openide.util.NbCollections;
 import org.openide.util.Parameters;
@@ -83,6 +85,15 @@ public ExecutorTask execute(AntProjectCookie antProject, String[] targets) throw
         te.setVerbosity(env.getVerbosity());
         te.setProperties(NbCollections.checkedMapByCopy(env.getProperties(), String.class, String.class, true));
         te.setConcealedProperties(env.getConcealedProperties());
+        if (env.shouldSaveAllDocs != null) {
+            te.setSaveAllDocuments(env.shouldSaveAllDocs);
+        }
+        te.setDisplayName(env.preferredName);
+        if (env.canReplace != null) {
+            assert env.canBeReplaced != null;
+            te.setTabReplaceStrategy(env.canReplace,env.canBeReplaced);
+        }
+        te.setUserAction(env.userAction);
         if (env.getLogger() == null) {
             return te.execute();
         } else {
@@ -100,6 +111,11 @@ public ExecutorTask execute(AntProjectCookie antProject, String[] targets) throw
         private Properties properties;
         private OutputStream outputStream;
         private volatile Set<String> concealedProperties;
+        private Boolean shouldSaveAllDocs;
+        private String preferredName;
+        private Predicate<String> canReplace;
+        private Predicate<String> canBeReplaced;
+        private boolean userAction;
 
         /** Create instance of Env class describing environment for Ant target execution.
          */
@@ -108,6 +124,7 @@ public Env() {
             properties = new Properties();
             properties.putAll(AntSettings.getProperties());
             concealedProperties = Collections.emptySet();
+            userAction = true;
         }
 
         /**
@@ -182,6 +199,56 @@ public void setConcealedProperties(@NonNull final Set<? extends String> properti
         public Set<String> getConcealedProperties() {
             return concealedProperties;
         }
+
+        /**
+         * Overrides the default save all behavior.
+         * @param shouldSave if true all modified documents are saved before running Ant.
+         * @since 3.84
+         */
+        public void setSaveAllDocuments(final boolean shouldSave) {
+            this.shouldSaveAllDocs = shouldSave;
+        }
+
+        /**
+         * Sets the preferred name for output windows.
+         * @param name the preferred name in case of null the name is assigned automatically
+         * @since 3.84
+         */
+        public void setPreferredName(@NullAllowed final String name) {
+            this.preferredName = name;
+        }
+
+        /**
+         * Sets the output tab replacement strategy.
+         * When the IDE is set to the automatic close tabs mode the tabs created by the previous
+         * run of the {@link AntTargetExecutor} are closed by successive run. This behavior can be overridden
+         * by this method.
+         * @param canReplace the {@link Predicate} used to decide if this execution
+         * can replace existing tab. The predicate parameter is a name of tab being replaced.
+         * @param canBeReplaced the {@link Predicate} used to decide if tab can be
+         * replaced by a new execution. The predicate parameter is a name of a tab being created.
+         * @since 3.84
+         */
+        public void setTabReplaceStrategy(
+                @NonNull final Predicate<String> canReplace,
+            @NonNull final Predicate<String> canBeReplaced) {
+            Parameters.notNull("canReplace", canReplace);   //NOI18N
+            Parameters.notNull("canBeReplaced", canBeReplaced); //NOI18N
+            this.canReplace = canReplace;
+            this.canBeReplaced = canBeReplaced;
+        }
+
+        /**
+         * Marks the execution as an user action.
+         * The executions marked as user actions are registered in the
+         * UI support {@link org.netbeans.spi.project.ui.support.BuildExecutionSupport}.
+         * By default the execution is an user action.
+         * @param userAction if true the execution is registered into
+         * the {@link org.netbeans.spi.project.ui.support.BuildExecutionSupport}
+         * @since 3.85
+         */
+        public void setUserAction(final boolean userAction) {
+            this.userAction = userAction;
+        }
     }
-    
 }
diff --git a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/LastTargetExecuted.java b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/LastTargetExecuted.java
index 3f47f6521..0af225fe7 100644
--- a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/LastTargetExecuted.java
+++ b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/LastTargetExecuted.java
@@ -23,10 +23,14 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
 import org.apache.tools.ant.module.AntModule;
 import org.apache.tools.ant.module.api.AntProjectCookie;
 import org.apache.tools.ant.module.api.support.AntScriptUtils;
 import org.apache.tools.ant.module.bridge.AntBridge;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.spi.project.ui.support.BuildExecutionSupport;
 import org.openide.execution.ExecutorTask;
 import org.openide.filesystems.FileObject;
@@ -40,32 +44,57 @@
  */
 public class LastTargetExecuted implements BuildExecutionSupport.ActionItem {
     
-    private LastTargetExecuted() {}
+    private LastTargetExecuted() {
+    }
     
     private File buildScript;
     //private static int verbosity;
     private String[] targets;
     private Map<String,String> properties;
+    private Set<String> concealedProperties;
     private String displayName;
     private Thread thread;
-    
+    private Boolean shouldSaveAllDocs;
+    private Predicate<String> canReplace;
+    private Predicate<String> canBeReplaced;
+    private boolean wasRegistered;
+
     /** Called from {@link TargetExecutor}. */
-    static LastTargetExecuted record(File buildScript, String[] targets, Map<String,String> properties, String displayName, Thread thread) {
+    static LastTargetExecuted record(
+            File buildScript,
+            String[] targets,
+            Map<String,String> properties,
+            @NonNull final Set<String> concealedProperties,
+            String displayName,
+            @NullAllowed final Boolean shouldSaveAllDocs,
+            @NonNull final Predicate<String> canReplace,
+            @NonNull final Predicate<String> canBeReplaced,
+            Thread thread,
+            final boolean shouldRegister) {
         LastTargetExecuted rec = new LastTargetExecuted();
         rec.buildScript = buildScript;
         //LastTargetExecuted.verbosity = verbosity;
         rec.targets = targets;
         rec.properties = properties;
+        rec.concealedProperties = concealedProperties;
         rec.displayName = displayName;
         rec.thread = thread;
-        BuildExecutionSupport.registerRunningItem(rec);
+        rec.shouldSaveAllDocs = shouldSaveAllDocs;
+        rec.canReplace = canReplace;
+        rec.canBeReplaced = canBeReplaced;
+        if (shouldRegister) {
+            BuildExecutionSupport.registerRunningItem(rec);
+        }
+        rec.wasRegistered = shouldRegister;
         return rec;
     }
 
     static void finish(LastTargetExecuted exc) {
-        BuildExecutionSupport.registerFinishedItem(exc);
+        if (exc.wasRegistered) {
+            BuildExecutionSupport.registerFinishedItem(exc);
+        }
     }
-    
+
     /**
      * Get the last build script to be run.
      * @return the last-run build script, or null if nothing has been run yet (or the build script disappeared etc.)
@@ -94,7 +123,12 @@ public static ExecutorTask rerun(LastTargetExecuted exec) throws IOException {
         TargetExecutor t = new TargetExecutor(apc, exec.targets);
         //t.setVerbosity(verbosity);
         t.setProperties(exec.properties);
+        t.setConcealedProperties(exec.concealedProperties);
         t.setDisplayName(exec.displayName); // #140999: do not recalculate
+        if (exec.shouldSaveAllDocs != null) {
+            t.setSaveAllDocuments(exec.shouldSaveAllDocs);
+        }
+        t.setTabReplaceStrategy(exec.canReplace, exec.canBeReplaced);
         return t.execute();
     }
     
diff --git a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java
index a8e0bdd4a..c3f81ec58 100644
--- a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java
+++ b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java
@@ -36,6 +36,7 @@
 import java.util.WeakHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.swing.AbstractAction;
@@ -66,6 +67,7 @@
 import org.openide.util.Cancellable;
 import org.openide.util.Mutex;
 import org.openide.util.NbBundle;
+import org.openide.util.Pair;
 import org.openide.util.Parameters;
 import org.openide.util.RequestProcessor;
 import org.openide.util.io.ReaderInputStream;
@@ -87,7 +89,7 @@
      * Map from tab to tab display name.
      * @see "#43001"
      */
-    private static final Map<InputOutput,String> freeTabs = new WeakHashMap<InputOutput,String>();
+    private static final Map<InputOutput,Pair<String,Predicate<String>>> freeTabs = new WeakHashMap<>();
     
     /**
      * Display names of currently active processes.
@@ -104,6 +106,10 @@
     /** used for the tab etc. */
     private String displayName;
     private String suggestedDisplayName;
+    private Boolean shouldSaveAllDocs;
+    private Predicate<String> canReplace = (s) -> true;
+    private Predicate<String> canBeReplaced = (s) -> true;
+    private boolean userAction = true;
     private volatile Set<String> concealedProperties;
 
     /** targets may be null to indicate default target */
@@ -126,8 +132,25 @@ public void setConcealedProperties(@NonNull final Set<? extends String> conceale
         this.concealedProperties = Collections.unmodifiableSet(new HashSet<String>(concealedProperties));
     }
 
-    void setDisplayName(String n) {
-        suggestedDisplayName = n;
+    public void setSaveAllDocuments(boolean shouldSaveAllDocs) {
+        this.shouldSaveAllDocs = shouldSaveAllDocs;
+    }
+
+    public void setDisplayName(String n) {
+        this.suggestedDisplayName = n;
+    }
+
+    public void setTabReplaceStrategy(
+            @NonNull final Predicate<String> canReplace,
+            @NonNull final Predicate<String> canBeReplaced) {
+        Parameters.notNull("canReplace", canReplace);   //NOI18N
+        Parameters.notNull("canBeReplaced", canBeReplaced); //NOI18N
+        this.canReplace = canReplace;
+        this.canBeReplaced = canBeReplaced;
+    }
+    
+    public void setUserAction(final boolean userAction) {
+        this.userAction = userAction;
     }
     
     private static String getProcessDisplayName(AntProjectCookie pcookie, List<String> targetNames) {
@@ -207,6 +230,9 @@ public void actionPerformed(ActionEvent e) {
         private Map<String,String> properties;
         private Set<String> concealedProperties;
         private String displayName;
+        private Boolean shouldSaveAllDocs;
+        private Predicate<String> canReplace;
+        private Predicate<String> canBeReplaced;
 
         public RerunAction(TargetExecutor prototype, boolean withModifications) {
             this.withModifications = withModifications;
@@ -225,6 +251,9 @@ private void reinit(TargetExecutor prototype) {
             properties = prototype.properties;
             concealedProperties = prototype.concealedProperties;
             displayName = prototype.suggestedDisplayName;
+            shouldSaveAllDocs = prototype.shouldSaveAllDocs;
+            canReplace = prototype.canReplace;
+            canBeReplaced = prototype.canBeReplaced;
         }
 
         @Override
@@ -267,6 +296,10 @@ public void actionPerformed(ActionEvent e) {
                     if (displayName != null) {
                         exec.setDisplayName(displayName);
                     }
+                    if (shouldSaveAllDocs != null) {
+                        exec.setSaveAllDocuments(shouldSaveAllDocs);
+                    }
+                    exec.setTabReplaceStrategy(canReplace, canBeReplaced);
                     exec.execute();
                 }
             } catch (IOException x) {
@@ -339,23 +372,27 @@ public ExecutorTask execute () throws IOException {
             // OutputWindow
             if (AntSettings.getAutoCloseTabs()) { // #47753
             synchronized (freeTabs) {
-                for (Map.Entry<InputOutput,String> entry : freeTabs.entrySet()) {
+                final Set<InputOutput> retained = new HashSet<>();
+                for (Map.Entry<InputOutput,Pair<String,Predicate<String>>> entry : freeTabs.entrySet()) {
                     InputOutput free = entry.getKey();
-                    String freeName = entry.getValue();
+                    String freeName = entry.getValue().first();
+                    Predicate<String> freePredicate = entry.getValue().second();
                     if (io == null && freeName.equals(displayName)) {
                         // Reuse it.
                         io = free;
                         io.getOut().reset();
                         // Apparently useless and just prints warning: io.getErr().reset();
                         // useless: io.flushReader();
-                    } else {
+                    } else if (canReplace.test(freeName) && freePredicate.test(displayName)) {
                         // Discard it.
                         free.closeInputOutput();
                         stopActions.remove(free);
                         rerunActions.remove(free);
+                    } else {
+                        retained.add(free);
                     }
                 }
-                freeTabs.clear();
+                freeTabs.keySet().retainAll(retained);
             }
             }
             if (io == null) {
@@ -441,7 +478,7 @@ public void run () {
             }
         }
         
-        if (AntSettings.getSaveAll()) {
+        if (shouldSaveAllDocs != null ? shouldSaveAllDocs : AntSettings.getSaveAll()) {
             LifecycleManager.getDefault ().saveAll ();
         }
         
@@ -461,10 +498,17 @@ public void run () {
         }
 
         // #139185: do not record verbosity level; always pick it up from Ant Settings.
-        thisExec[0] = LastTargetExecuted.record(buildFile, /*verbosity,*/
+        thisExec[0] = LastTargetExecuted.record(
+                buildFile, /*verbosity,*/
                 targetNames != null ? targetNames.toArray(new String[targetNames.size()]) : null,
                 properties,
-                suggestedDisplayName != null ? suggestedDisplayName : getProcessDisplayName(pcookie, targetNames), Thread.currentThread());
+                concealedProperties,
+                suggestedDisplayName != null ? suggestedDisplayName : getProcessDisplayName(pcookie, targetNames),
+                shouldSaveAllDocs,
+                canReplace,
+                canBeReplaced,
+                Thread.currentThread(),
+                userAction);
         sa.t = thisExec[0];
         
         // Don't hog the CPU, the build might take a while:
@@ -532,7 +576,7 @@ public void actionPerformed(ActionEvent e) {
         } finally {
             if (io != null) {
                 synchronized (freeTabs) {
-                    freeTabs.put(io, displayName);
+                    freeTabs.put(io, Pair.of(displayName,canBeReplaced));
                 }
             }
             if (thisExec[0] != null) {
diff --git a/parsing.api/nbproject/project.properties b/parsing.api/nbproject/project.properties
index d7e86d991..36b7a665f 100644
--- a/parsing.api/nbproject/project.properties
+++ b/parsing.api/nbproject/project.properties
@@ -19,7 +19,7 @@ javac.compilerargs=-Xlint -Xlint:-serial
 javac.source=1.7
 javadoc.apichanges=${basedir}/apichanges.xml
 javadoc.arch=${basedir}/arch.xml
-spec.version.base=9.7.0
+spec.version.base=9.8.0
 
 test.config.stableBTD.includes=**/*Test.class
 test.config.stableBTD.excludes=\
diff --git a/parsing.api/src/org/netbeans/modules/parsing/api/Source.java b/parsing.api/src/org/netbeans/modules/parsing/api/Source.java
index b608e2c9f..1f3cacd0d 100644
--- a/parsing.api/src/org/netbeans/modules/parsing/api/Source.java
+++ b/parsing.api/src/org/netbeans/modules/parsing/api/Source.java
@@ -493,7 +493,7 @@ protected Boolean initialValue() {
     
     private int taskCount;
     private volatile Parser cachedParser;
-    private final AtomicReference<ASourceModificationEvent> sourceModificationEvent = new AtomicReference<ASourceModificationEvent>();
+    private final AtomicReference<ExtendableSourceModificationEvent> sourceModificationEvent = new AtomicReference<>();
     private final ASourceModificationEvent unspecifiedSourceModificationEvent = new ASourceModificationEvent (this, true, -1, -1);
     private Map<Class<? extends Scheduler>, SchedulerEvent> schedulerEvents;
     //GuardedBy(this)
@@ -570,12 +570,15 @@ private void setFlags(final Set<SourceFlags> flags) {
     }
 
     private void setSourceModification (boolean sourceChanged, int startOffset, int endOffset) {
-        ASourceModificationEvent oldSourceModificationEvent;
-        ASourceModificationEvent newSourceModificationEvent;
+        ExtendableSourceModificationEvent oldSourceModificationEvent;
+        ExtendableSourceModificationEvent newSourceModificationEvent;
         do {
             oldSourceModificationEvent = sourceModificationEvent.get();
-            boolean mergedChange = sourceChanged | (oldSourceModificationEvent == null ? false : oldSourceModificationEvent.sourceChanged());
-            newSourceModificationEvent = new ASourceModificationEvent (this, mergedChange, startOffset, endOffset);                
+            if (oldSourceModificationEvent == null) {
+                newSourceModificationEvent = new ASourceModificationEvent (this, sourceChanged, startOffset, endOffset);
+            } else {
+                newSourceModificationEvent = oldSourceModificationEvent.add(sourceChanged, startOffset, endOffset);
+            }
         } while (!sourceModificationEvent.compareAndSet(oldSourceModificationEvent, newSourceModificationEvent));
     }
 
@@ -733,7 +736,7 @@ public void parsed (Source source) {
         @Override
         public SourceModificationEvent getSourceModificationEvent (Source source) {
             assert source != null;
-            SourceModificationEvent event = source.sourceModificationEvent.get();
+            SourceModificationEvent event = (SourceModificationEvent) source.sourceModificationEvent.get();
             if (event == null) {
                 event = source.unspecifiedSourceModificationEvent;
             }
@@ -865,11 +868,13 @@ public Source create(@NonNull final FileObject file, @NonNull final String mimeT
             return new Source(mimeType, null, file, context);
         }
     } // End of MySourceAccessor class
-        
-    private static class ASourceModificationEvent extends SourceModificationEvent {
+    private static interface ExtendableSourceModificationEvent {
+         ExtendableSourceModificationEvent add(final boolean changed, int start, int end);
+    }
+    private static class ASourceModificationEvent extends SourceModificationEvent implements ExtendableSourceModificationEvent {
 
-        private int         startOffset;
-        private int         endOffset;
+        private final int         startOffset;
+        private final int         endOffset;
 
         ASourceModificationEvent (
             Object          source,
@@ -881,13 +886,50 @@ public Source create(@NonNull final FileObject file, @NonNull final String mimeT
             startOffset = _startOffset;
             endOffset = _endOffset;
         }
-        
-        void add (
-            int             _startOffset,
-            int             _endOffset
-        ) {
-            startOffset = Math.min (startOffset, _startOffset);
-            endOffset = Math.max (endOffset, _endOffset);
+
+        @Override
+        public int getAffectedStartOffset() {
+            return startOffset;
+        }
+
+        @Override
+        public int getAffectedEndOffset() {
+            return endOffset;
+        }
+
+        @Override
+        public String toString () {
+            //XXX: Never change the toString value, some tests depends on it!
+            return "SourceModificationEvent " + startOffset + ":" + endOffset;
+        }
+
+        @NonNull
+        @Override
+        public ExtendableSourceModificationEvent add(final boolean changed, int start, int end) {
+            final boolean oldChanged = sourceChanged();
+            if (oldChanged == changed) {
+                start = Math.min(start, startOffset);
+                end = Math.min(end, endOffset);
+                return new ASourceModificationEvent (getSource(), oldChanged, start, end);
+            } else {
+                final ASourceModificationEvent other = new ASourceModificationEvent(getSource(), changed, start, end);
+                return oldChanged ?
+                        new AComposite(other, this) :
+                        new AComposite(this, other);
+            }
+        }
+    }
+
+    private static class AComposite extends SourceModificationEvent.Composite implements ExtendableSourceModificationEvent {
+        private final int startOffset;
+        private final int endOffset;
+
+        AComposite(
+                @NonNull final ASourceModificationEvent read,
+                @NonNull final ASourceModificationEvent write) {
+            super(read, write);
+            this.startOffset = Math.min(read.getAffectedStartOffset(), write.getAffectedStartOffset());
+            this.endOffset = Math.max(read.getAffectedEndOffset(), write.getAffectedEndOffset());
         }
 
         @Override
@@ -905,6 +947,26 @@ public String toString () {
             //XXX: Never change the toString value, some tests depends on it!
             return "SourceModificationEvent " + startOffset + ":" + endOffset;
         }
+
+        @Override
+        public ASourceModificationEvent getWriteEvent() {
+            return (ASourceModificationEvent) super.getWriteEvent();
+        }
+
+        @Override
+        public ASourceModificationEvent getReadEvent() {
+            return (ASourceModificationEvent) super.getReadEvent();
+        }
+
+        @NonNull
+        @Override
+        public ExtendableSourceModificationEvent add(boolean changed, int start, int end) {
+            if (changed) {
+                return new AComposite(getReadEvent(), (ASourceModificationEvent)getWriteEvent().add(changed, start, end));
+            } else {
+                return new AComposite((ASourceModificationEvent)getReadEvent().add(changed, start, end), getWriteEvent());
+            }
+        }
     }
 
     /**
diff --git a/parsing.api/src/org/netbeans/modules/parsing/spi/SourceModificationEvent.java b/parsing.api/src/org/netbeans/modules/parsing/spi/SourceModificationEvent.java
index 9959227bc..516cb89d9 100644
--- a/parsing.api/src/org/netbeans/modules/parsing/spi/SourceModificationEvent.java
+++ b/parsing.api/src/org/netbeans/modules/parsing/spi/SourceModificationEvent.java
@@ -20,7 +20,9 @@
 package org.netbeans.modules.parsing.spi;
 
 import java.util.EventObject;
+import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.modules.parsing.api.Source;
+import org.openide.util.Parameters;
 
 /**
  *
@@ -91,4 +93,30 @@ public int getAffectedEndOffset() {
     public String toString () {
         return "SourceModificationEvent " + hashCode () + "(source: " + source + ")";
     }
+
+    /**
+     * @since 9.8.0
+     */
+    public static class Composite extends SourceModificationEvent {
+        private final SourceModificationEvent read;
+        private final SourceModificationEvent write;
+
+        public Composite(
+                @NonNull final SourceModificationEvent read,
+                @NonNull SourceModificationEvent write) {
+            super(read.getSource(), true);
+            Parameters.notNull("read", read);   //NOI18N
+            Parameters.notNull("write", write);   //NOI18N
+            this.read = read;
+            this.write = write;
+        }
+
+        public SourceModificationEvent getWriteEvent() {
+            return write;
+        }
+
+        public SourceModificationEvent getReadEvent() {
+            return read;
+        }
+    }
 }
diff --git a/parsing.indexing/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java b/parsing.indexing/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java
index 606c7e7d1..c571f36dc 100644
--- a/parsing.indexing/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java
+++ b/parsing.indexing/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java
@@ -3256,44 +3256,55 @@ protected final boolean scanFiles(
                         @Override
                         public Boolean call() throws Exception {
                             final ClassPath.Entry entry = sourceForBinaryRoot ? null : getClassPathEntry(rootFo);
-                            final Set<Crawler.TimeStampAction> checkTimeStamps = EnumSet.noneOf(Crawler.TimeStampAction.class);
                             final boolean permanentUpdate = isSteady();
                             assert !TransientUpdateSupport.isTransientUpdate() || !permanentUpdate;
                             assert permanentUpdate || (forceRefresh && !files.isEmpty());
-                            if (!forceRefresh) {
-                                checkTimeStamps.add(Crawler.TimeStampAction.CHECK);
-                            }
-                            if (permanentUpdate) {
-                                checkTimeStamps.add(Crawler.TimeStampAction.UPDATE);
-                            }
-                            final Crawler crawler = files.isEmpty() ?
-                                new FileObjectCrawler(rootFo, checkTimeStamps, entry, getCancelRequest(), getSuspendStatus()) : // rescan the whole root (no timestamp check)
-                                new FileObjectCrawler(rootFo, files.toArray(new FileObject[files.size()]), checkTimeStamps, entry, getCancelRequest(), getSuspendStatus()); // rescan selected files (no timestamp check)
+
+                            final SourceIndexers indexers = getSourceIndexers(false);
+                            final Map<Pair<String,Integer>,Pair<SourceIndexerFactory,Context>> ctxToFinish = new HashMap<>();
+                            final Map<SourceIndexerFactory,Boolean> invalidatedMap = new IdentityHashMap<>();
+                            final UsedIndexables usedIterables = new UsedIndexables();
+                            boolean indexResult = false;
                             if (lctx != null) {
-                                lctx.noteRootScanning(root, true);
+                                lctx.noteRootScanning(root, false);
                             }
-                            long t = System.currentTimeMillis();
-                            final List<Indexable> resources = crawler.getResources();
-                            if (crawler.isFinished()) {
-
-                                logCrawlerTime(crawler, t);
-                                final Map<SourceIndexerFactory,Boolean> invalidatedMap = new IdentityHashMap<>();
-                                final Map<Pair<String,Integer>,Pair<SourceIndexerFactory,Context>> ctxToFinish = new HashMap<>();
-                                final UsedIndexables usedIterables = new UsedIndexables();
-                                final SourceIndexers indexers = getSourceIndexers(false);
-                                invalidateSources(resources);
-                                boolean indexResult=false;
-                                try {
-                                    scanStarted (root, sourceForBinaryRoot, indexers, invalidatedMap, ctxToFinish);
+                            try {
+                                scanStarted (root, sourceForBinaryRoot, indexers, invalidatedMap, ctxToFinish);
+                                boolean indexerVeto = false;
+                                for (Boolean b : invalidatedMap.values()) {
+                                    if (!b) {
+                                        indexerVeto = true;
+                                        break;
+                                    }
+                                }
+                                //Find files to index
+                                final Set<Crawler.TimeStampAction> checkTimeStamps = EnumSet.noneOf(Crawler.TimeStampAction.class);
+                                if (!forceRefresh) {
+                                    checkTimeStamps.add(Crawler.TimeStampAction.CHECK);
+                                }
+                                if (permanentUpdate) {
+                                    checkTimeStamps.add(Crawler.TimeStampAction.UPDATE);
+                                }
+                                final Crawler crawler = files.isEmpty() || indexerVeto ?
+                                    new FileObjectCrawler(rootFo, checkTimeStamps, entry, getCancelRequest(), getSuspendStatus()) : // rescan the whole root (no timestamp check)
+                                    new FileObjectCrawler(rootFo, files.toArray(new FileObject[files.size()]), checkTimeStamps, entry, getCancelRequest(), getSuspendStatus()); // rescan selected files (no timestamp check)
+                                if (lctx != null) {
+                                    lctx.startCrawler();
+                                }
+                                long t = System.currentTimeMillis();
+                                final List<Indexable> resources = crawler.getResources();
+                                if (crawler.isFinished()) {
+                                    logCrawlerTime(crawler, t);
+                                    invalidateSources(resources);
                                     delete(crawler.getDeletedResources(), ctxToFinish, usedIterables);
-                                    indexResult=index(resources, crawler.getAllResources(), root, sourceForBinaryRoot, indexers, invalidatedMap, ctxToFinish, usedIterables);
+                                    indexResult = index(resources, crawler.getAllResources(), root, sourceForBinaryRoot, indexers, invalidatedMap, ctxToFinish, usedIterables);
                                     if (indexResult) {
                                         crawler.storeTimestamps();
                                         return true;
                                     }
-                                } finally {
-                                    scanFinished(ctxToFinish.values(), usedIterables, indexResult);
                                 }
+                            } finally {
+                                 scanFinished(ctxToFinish.values(), usedIterables, indexResult);
                             }
                             return false;
                         }
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java
index 70c95e764..e0a485492 100644
--- a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java
@@ -529,7 +529,7 @@ private static String getStartText(final String regexp) {
                         i++;
                         continue;
                     }
-                } else if (!quoted && (c == '^') || c == '$') { //NOI18N
+                } else if (!quoted && (c == '^' || c == '$')) { //NOI18N
                     continue;
                 }
                 if (!quoted && SPECIAL_CHARS.get(c)) {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services