Commits

Anonymous committed e1db8f6 Draft Merge

Automated merge with main-silver

Comments (0)

Files changed (34)

java.hints/src/org/netbeans/modules/java/hints/OrganizeImports.java

             }
         }
         final CodeStyle cs = CodeStyle.getDefault(copy.getFileObject());
-        Set<Element> starImports = cs.countForUsingStarImport() < Integer.MAX_VALUE ? new HashSet<Element>() : null;
-        Set<Element> staticStarImports = cs.countForUsingStaticStarImport() < Integer.MAX_VALUE ? new HashSet<Element>() : null;
+        Set<Element> starImports = new HashSet<Element>();
+        Set<Element> staticStarImports = new HashSet<Element>();
         Set<Element> toImport = getUsedElements(copy, cu, starImports, staticStarImports);
         List<ImportTree> imps = new LinkedList<ImportTree>();  
         TreeMaker maker = copy.getTreeMaker();
             toImport.addAll(addImports);
             imps.addAll(cu.getImports());
         } else if (!toImport.isEmpty() || isBulkMode) {
-            if (starImports != null || staticStarImports != null) {
-                Trees trees = copy.getTrees();
-                for (ImportTree importTree : cu.getImports()) {
-                    Tree qualIdent = importTree.getQualifiedIdentifier();
-                    if (qualIdent.getKind() == Tree.Kind.MEMBER_SELECT && "*".contentEquals(((MemberSelectTree)qualIdent).getIdentifier())) {
-                        if (importTree.isStatic()) {
-                            if (staticStarImports != null && staticStarImports.contains(trees.getElement(TreePath.getPath(cu, ((MemberSelectTree)qualIdent).getExpression()))))
-                                imps.add(maker.Import(qualIdent, true));
-                        } else {
-                            if (starImports != null && starImports.contains(trees.getElement(TreePath.getPath(cu, ((MemberSelectTree)qualIdent).getExpression()))))
-                                imps.add(maker.Import(qualIdent, false));
-                        }
+            Trees trees = copy.getTrees();
+            for (ImportTree importTree : cu.getImports()) {
+                Tree qualIdent = importTree.getQualifiedIdentifier();
+                if (qualIdent.getKind() == Tree.Kind.MEMBER_SELECT && "*".contentEquals(((MemberSelectTree)qualIdent).getIdentifier())) {
+                    if (importTree.isStatic()) {
+                        if (staticStarImports != null && staticStarImports.contains(trees.getElement(TreePath.getPath(cu, ((MemberSelectTree)qualIdent).getExpression()))))
+                            imps.add(maker.Import(qualIdent, true));
+                    } else {
+                        if (starImports != null && starImports.contains(trees.getElement(TreePath.getPath(cu, ((MemberSelectTree)qualIdent).getExpression()))))
+                            imps.add(maker.Import(qualIdent, false));
                     }
                 }
-            } else {
-                imps = Collections.emptyList();
             }
         } else {
             return;

java.hints/src/org/netbeans/modules/java/hints/OrganizeMembers.java

 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import javax.swing.text.Document;
 import javax.swing.text.JTextComponent;
         TreeMaker maker = copy.getTreeMaker();
         ClassTree nue = maker.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), Collections.<Tree>emptyList());
         List<Tree> members = new ArrayList<Tree>(clazz.getMembers().size());
+        Map<Tree, Tree> memberMap = new HashMap<>(clazz.getMembers().size());
         for (Tree tree : clazz.getMembers()) {
             if (copy.getTreeUtilities().isSynthetic(new TreePath(path, tree))) continue;
             Tree member;
                     member = tree;    
             }
             members.add(member);
+            memberMap.put(member, tree);
         }
+        // fool the generator utilities with cloned members, so it does not take positions into account
         nue = GeneratorUtilities.get(copy).insertClassMembers(nue, members);
-        copy.rewrite(clazz, nue);
+        // now create a new class, based on the original one - retain the order decided by GeneratorUtilities.
+        ClassTree changed = maker.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), Collections.<Tree>emptyList());
+        int index = 0;
+        for (Tree t : nue.getMembers()) {
+            Tree orig = memberMap.get(t);
+            changed = maker.insertClassMember(changed, index, orig);
+            index++;
+        }
+        copy.rewrite(clazz, changed);
     }
     
     private static boolean checkGuarded(Document doc, List<? extends Difference> diffs) {

java.hints/src/org/netbeans/modules/java/hints/errors/ExtraCatch.java

             
             if (parent.getResources().isEmpty() && parent.getCatches().size() == 1) {
                 Tree parentParent = ctx.getPath().getParentPath().getParentPath().getLeaf();
+                List<StatementTree> statements;
+                BlockTree parentBlock = null;
+                CaseTree parentCase = null;
                 
                 switch (parentParent.getKind()) {
                     case BLOCK: {
-                        BlockTree parentBlock = (BlockTree) parentParent;
-                        List<StatementTree> statements = new ArrayList<>(parentBlock.getStatements());
-                        statements.addAll(statements.indexOf(parent), parent.getBlock().getStatements());
-                        statements.remove(parent);
-                        ctx.getWorkingCopy().rewrite(parentParent, make.Block(statements, parentBlock.isStatic()));
-                        return ;
+                        parentBlock = (BlockTree) parentParent;
+                        statements = new ArrayList<>(parentBlock.getStatements());
+                        break;
                     }
                     case CASE: {
-                        CaseTree parentCase = (CaseTree) parentParent;
-                        List<StatementTree> statements = new ArrayList<>(parentCase.getStatements());
-                        statements.addAll(statements.indexOf(parent), parent.getBlock().getStatements());
-                        statements.remove(parent);
-                        ctx.getWorkingCopy().rewrite(parentParent, make.Case(parentCase.getExpression(), statements));
-                        return ;
+                        parentCase = (CaseTree) parentParent;
+                        statements = new ArrayList<>(parentCase.getStatements());
+                        break;
                     }
+                    default:
+                        throw new IllegalStateException();
                 }
+                int at = statements.indexOf(parent);
+                statements.addAll(at, parent.getBlock().getStatements());
+                if (parent.getFinallyBlock() != null) {
+                    statements.addAll(at + parent.getBlock().getStatements().size(), parent.getFinallyBlock().getStatements());
+                }
+                statements.remove(parent);
+                Tree stat;
+                
+                if (parentParent.getKind() == Tree.Kind.BLOCK) {
+                    stat = make.Block(statements, parentBlock.isStatic());
+                } else {
+                    stat = make.Case(parentCase.getExpression(), statements);
+                }
+                ctx.getWorkingCopy().rewrite(parentParent, stat);
+            } else {
+                ctx.getWorkingCopy().rewrite(parent, make.removeTryCatch(parent, toRemove));
             }
-            
-            ctx.getWorkingCopy().rewrite(parent, make.removeTryCatch(parent, toRemove));
         }
         
     }

java.source/src/org/netbeans/api/java/source/GeneratorUtilities.java

         return make.Method(make.Modifiers(mods), setterName, make.Type(copy.getTypes().getNoType(TypeKind.VOID)), Collections.<TypeParameterTree>emptyList(), params, Collections.<ExpressionTree>emptyList(), body, null);
     }
     
+    private boolean isStarImport(ImportTree imp) {
+        Tree qualIdent = imp.getQualifiedIdentifier();        
+        boolean isStar = qualIdent.getKind() == Tree.Kind.MEMBER_SELECT && ((MemberSelectTree)qualIdent).getIdentifier().contentEquals("*"); // NOI18N
+        return isStar;
+    }
+    
     /**
      * Adds import statements for given elements to a compilation unit. The import section of the
      * given compilation unit is modified according to the rules specified in the {@link CodeStyle}.
         int staticTreshold = cs.countForUsingStaticStarImport();        
         Map<PackageElement, Integer> pkgCounts = new LinkedHashMap<PackageElement, Integer>();
         PackageElement pkg = elements.getPackageElement("java.lang"); //NOI18N
-        if (pkg != null)
+        if (pkg != null) {
             pkgCounts.put(pkg, -2);
+        }
         ExpressionTree packageName = cut.getPackageName();
         pkg = packageName != null ? (PackageElement)trees.getElement(TreePath.getPath(cut, packageName)) : null;
-        if (pkg == null && packageName != null)
+        if (pkg == null && packageName != null) {
             pkg = elements.getPackageElement(elements.getName(packageName.toString()));
-        if (pkg == null)
+        }
+        if (pkg == null) {
             pkg = elements.getPackageElement(elements.getName("")); //NOI18N
+        }
         pkgCounts.put(pkg, -2);
         Map<TypeElement, Integer> typeCounts = new LinkedHashMap<TypeElement, Integer>();
         // initially the import scope has no symbols. We must fill it in by:
         // existing CUT named imports, package members AND then star imports, in this specific order
         JCCompilationUnit jcut = (JCCompilationUnit)cut;
         StarImportScope importScope = new StarImportScope((Symbol)pkg);
-        if (jcut.starImportScope != null)
+        if (jcut.starImportScope != null) {
             importScope.importAll(((JCCompilationUnit)cut).starImportScope);
+        }
         if (jcut.packge != null) {
             importScope.importAll(jcut.packge.members_field);
         }
-        if (jcut.namedImportScope != null) {
-            importScope.importAll(jcut.namedImportScope);
-        }
         for (Element e : elementsToImport) {
             boolean isStatic = false;
             Element el = null;
                         Element el = e;
                         while (el != null) {
                             Integer cnt = typeCounts.get((TypeElement)el);
-                            if (cnt != null) {
+                            if (cnt != null && staticTreshold == Integer.MAX_VALUE) {
                                 typeCounts.put((TypeElement)el, -2);
                             }
                             TypeMirror tm = ((TypeElement)el).getSuperclass();
                     if (el != null) {
                         Integer cnt = pkgCounts.get((PackageElement)el);
                         if (cnt != null) {
-                            if (el == e) {
-                                cnt = -2;
+                            if (el == e) { // this is only true for package element, that is for package-star import.
+                                if (treshold == Integer.MAX_VALUE) {
+                                    // do not touch the star import
+                                    cnt = -2;
+                                }
                             } else if (cnt >= 0) {
                                 cnt++;
                                 if (cnt >= treshold)
                         }
                     }
                 }
+            } else if (treshold == Integer.MAX_VALUE || staticTreshold == Integer.MAX_VALUE) {
+                // disable any manipulations (optimization) for existing star imports iff the "Count" feature is disabled.
+                int threshold = imp.isStatic() ? staticTreshold : treshold;
+                if (isStarImport(imp) && threshold == Integer.MAX_VALUE) {
+                    Map map = imp.isStatic() ? typeCounts : pkgCounts;
+                    Integer cnt = (Integer)map.get(e);
+                    if (cnt != null) {
+                        map.put(e, -2);
+                    }
+                }
+            }
+        }
+        // remove those star imports that do not satisfy the thresholds
+        for (Iterator<ImportTree> ii = imports.iterator(); ii.hasNext();) {
+            ImportTree imp = ii.next();
+            if (!isStarImport(imp)) {
+                continue;
+            }
+            Element e = getImportedElement(cut, imp);
+            Integer cnt;
+            if (imp.isStatic()) {
+                cnt = typeCounts.get(e);
+            } else {
+                cnt = pkgCounts.get(e);
+            }
+            if (cnt != null && cnt >= 0) {
+                ii.remove();
             }
         }
         
                 }
                 boolean isStar = currentToImportElement.getKind() == ElementKind.PACKAGE
                         || isStatic && (currentToImportElement.getKind().isClass() || currentToImportElement.getKind().isInterface());
+                ExpressionTree qualIdent = qualIdentFor(currentToImportElement);
+                if (isStar) {
+                    qualIdent = make.MemberSelect(qualIdent, elements.getName("*")); //NOI18N
+                }
+                ImportTree nImport = make.Import(qualIdent, isStatic);
                 while (currentExisting >= 0) {
                     ImportTree imp = imports.get(currentExisting);
                     Element impElement = getImportedElement(cut, imp);
                             : impElement.getKind() == ElementKind.PACKAGE ? impElement : (impElement.getKind().isClass() || impElement.getKind().isInterface()) && impElement.getEnclosingElement().getKind() == ElementKind.PACKAGE ? impElement.getEnclosingElement() : null;
                     if (isStatic == imp.isStatic() && (currentToImportElement == impElement || isStar && currentToImportElement == el)) {
                         imports.remove(currentExisting);                        
-                    } else if (comparator.compare(currentToImportElement, imp) > 0) {
-                        break;
+                    } else {
+                        if (comparator.compare(nImport, imp) > 0) {
+                            break;
+                        }
                     }
                     currentExisting--;
                 }
-                ExpressionTree qualIdent = qualIdentFor(currentToImportElement);
-                if (isStar)
-                    qualIdent = make.MemberSelect(qualIdent, elements.getName("*")); //NOI18N
-                imports.add(currentExisting + 1, make.Import(qualIdent, isStatic));
+                imports.add(currentExisting + 1, nImport);
                 currentToImport--;
             }
         }
                 return parent;
             }
         }
-        Element element = trees.getElement(TreePath.getPath(cut, qualIdent));
+        TreePath found = TreePath.getPath(cut, qualIdent);
+        if (found == null) {
+            found = new TreePath(new TreePath(new TreePath(cut), imp), qualIdent);
+        }
+        Element element = trees.getElement(found);
         if (element == null)
             element = getElementByFQN(qualIdent.toString());
         return element;

java.source/src/org/netbeans/modules/java/source/save/CasualDiff.java

                         break;
                     }
                     if (LineInsertionType.BEFORE == estimator.lineInsertType()) printer.newline();
-                    // PENDING: although item.element may be among oldTrees, its surrounding whitespaces are not used
-                    // at all
-                    printer.print(item.element);
+                    // specific case: the item is shuffled within the same parent. It's not expected that the printer will print it with the usual 
+                    // codestyle-defined blanklines before/after. However the blank lines are more expected when the item shifts to another scope.
+                    if (!oldList.contains(item.element) || !printer.handlePossibleOldTrees(Collections.singletonList(item.element), true)) {
+                        printer.print(item.element);
+                    }
                     if (LineInsertionType.AFTER == estimator.lineInsertType()) printer.newline();
                     break;
                 }

php.api.documentation/manifest.mf

 OpenIDE-Module: org.netbeans.modules.php.api.documentation/0
 OpenIDE-Module-Layer: org/netbeans/modules/php/api/documentation/resources/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/api/documentation/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 0.11
+OpenIDE-Module-Specification-Version: 0.12

php.api.documentation/nbproject/project.xml

                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>2.44</specification-version>
+                        <specification-version>2.45</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>

php.api.documentation/src/org/netbeans/modules/php/api/documentation/ui/actions/BaseSubMenu.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.php.api.documentation.ui.actions;
+
+import javax.swing.Action;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import org.openide.awt.Actions;
+import org.openide.util.actions.Presenter;
+
+abstract class BaseSubMenu extends JMenu {
+
+    public BaseSubMenu(String name) {
+        super(name);
+    }
+
+    protected static JMenuItem toMenuItem(Action action) {
+        JMenuItem item;
+        if (action instanceof Presenter.Menu) {
+            item = ((Presenter.Menu) action).getMenuPresenter();
+        } else {
+            item = new JMenuItem();
+            Actions.connect(item, action, false);
+        }
+        return item;
+    }
+
+}

php.api.documentation/src/org/netbeans/modules/php/api/documentation/ui/actions/GenerateDocumentationActionFactory.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.php.api.documentation.ui.actions;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JMenuItem;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.php.api.documentation.PhpDocumentations;
+import org.netbeans.modules.php.api.phpmodule.PhpModule;
+import org.netbeans.modules.php.api.util.UiUtils;
+import org.netbeans.modules.php.spi.documentation.PhpDocumentationProvider;
+import org.openide.LifecycleManager;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionRegistration;
+import org.openide.awt.DynamicMenuContent;
+import org.openide.util.ContextAwareAction;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+import org.openide.util.actions.Presenter;
+
+/**
+ * Add 'Generate documentation' menu (for 1 provider) or submenu (for more providers). Do nothing if there are no providers.
+ */
+@NbBundle.Messages("GenerateDocumentationActionFactory.title=Generate Documentation")
+@ActionID(id = "org.netbeans.modules.php.api.documentation.ui.actions.GenerateDocumentationActionFactory", category = "Project")
+@ActionRegistration(displayName = "#GenerateDocumentationActionFactory.title", lazy = false)
+@ActionReference(position = 800, path = "Projects/org-netbeans-modules-php-phpproject/Actions")
+public final class GenerateDocumentationActionFactory extends AbstractAction implements ContextAwareAction {
+
+    private static final long serialVersionUID = 5687856454545L;
+
+
+    public GenerateDocumentationActionFactory() {
+        setEnabled(false);
+        putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        assert false;
+    }
+
+    @Override
+    public Action createContextAwareInstance(Lookup actionContext) {
+        Collection<? extends Project> projects = actionContext.lookupAll(Project.class);
+        if (projects.size() != 1) {
+            return this;
+        }
+        Project project = projects.iterator().next().getLookup().lookup(Project.class);
+        if (project == null) {
+            return this;
+        }
+        List<PhpDocumentationProvider> docProviders = PhpDocumentations.getDocumentations();
+        if (docProviders.isEmpty()) {
+            return this;
+        }
+
+        PhpModule phpModule = PhpModule.Factory.lookupPhpModule(project);
+        if (phpModule == null) {
+            return this;
+        }
+        List<PhpDocumentationProvider> projectDocProviders = new ArrayList<>(docProviders.size());
+        for (PhpDocumentationProvider docProvider : docProviders) {
+            if (docProvider.isInPhpModule(phpModule)) {
+                projectDocProviders.add(docProvider);
+            }
+        }
+        if (projectDocProviders.isEmpty()) {
+            // no provider selected yet -> show all
+            projectDocProviders.addAll(docProviders);
+        }
+        if (projectDocProviders.size() == 1) {
+            return new PhpDocAction(phpModule, projectDocProviders.get(0));
+        }
+        return new DocumentationMenu(phpModule, projectDocProviders);
+    }
+
+    //~ Inner classes
+
+    private static final class DocumentationMenu extends AbstractAction implements Presenter.Popup {
+
+        private static final long serialVersionUID = 1587896543546879L;
+
+        private final PhpModule phpModule;
+        private final List<PhpDocumentationProvider> docProviders;
+
+
+        public DocumentationMenu(PhpModule phpModule, List<PhpDocumentationProvider> docProviders) {
+            super(Bundle.GenerateDocumentationActionFactory_title(), null);
+            assert phpModule != null;
+            assert docProviders != null;
+
+            putValue(SHORT_DESCRIPTION, Bundle.GenerateDocumentationActionFactory_title());
+            this.phpModule = phpModule;
+            this.docProviders = docProviders;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            assert false;
+        }
+
+        @Override
+        public JMenuItem getPopupPresenter() {
+            List<PhpDocAction> docActions = new ArrayList<>(docProviders.size());
+            for (PhpDocumentationProvider docProvider : docProviders) {
+                docActions.add(new PhpDocAction(docProvider.getDisplayName(), phpModule, docProvider, true));
+            }
+            return new DocumentationSubMenu(docActions);
+        }
+
+    }
+
+    private static class DocumentationSubMenu extends BaseSubMenu {
+
+        private static final long serialVersionUID = -6764324657641L;
+
+
+        public DocumentationSubMenu(List<PhpDocAction> docActions) {
+            super(Bundle.GenerateDocumentationActionFactory_title());
+
+            for (PhpDocAction action : docActions) {
+                add(toMenuItem(action));
+            }
+        }
+
+    }
+
+    private static final class PhpDocAction extends AbstractAction {
+
+        private static final long serialVersionUID = 178423135454L;
+
+        private static final RequestProcessor RP = new RequestProcessor("Generating php documentation", 2); // NOI18N
+
+        private final PhpModule phpModule;
+        private final PhpDocumentationProvider docProvider;
+        private final boolean remember;
+
+
+        public PhpDocAction(PhpModule phpModule, PhpDocumentationProvider docProvider) {
+            this(Bundle.GenerateDocumentationActionFactory_title(), phpModule, docProvider, false);
+        }
+
+        public PhpDocAction(String name, PhpModule phpModule, PhpDocumentationProvider docProvider, boolean remember) {
+            this.phpModule = phpModule;
+            this.docProvider = docProvider;
+            this.remember = remember;
+
+            putValue(NAME, name);
+            putValue(SHORT_DESCRIPTION, name);
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            if (phpModule.isBroken()) {
+                // broken project
+                UiUtils.warnBrokenProject(phpModule);
+                return;
+            }
+            RP.post(new Runnable() {
+                @Override
+                public void run() {
+                    LifecycleManager.getDefault().saveAll();
+                    if (remember) {
+                        // remember curent provider
+                        docProvider.notifyEnabled(phpModule, true);
+                    }
+                    docProvider.generateDocumentation(phpModule);
+                }
+
+            });
+        }
+
+    }
+
+}

php.api.documentation/src/org/netbeans/modules/php/api/documentation/ui/customizer/CustomizerDocumentation.java

     private final ProjectCustomizer.Category category;
     private final PhpModule phpModule;
     private final Map<PhpDocumentationProvider, PhpModuleCustomizer> providerPanels;
+    private final PhpDocumentationProvider originalProvider;
+
+    private volatile PhpDocumentationProvider selectedProvider;
 
 
     CustomizerDocumentation(ProjectCustomizer.Category category, PhpModule phpModule) {
 
         this.category = category;
         this.phpModule = phpModule;
+        originalProvider = getOriginalProvider();
 
         providerPanels = createProviderPanels();
 
         init();
     }
 
+    private PhpDocumentationProvider getOriginalProvider() {
+        for (PhpDocumentationProvider provider : PhpDocumentations.getDocumentations()) {
+            if (provider.isInPhpModule(phpModule)) {
+                return provider;
+            }
+        }
+        return null;
+    }
+
     private Map<PhpDocumentationProvider, PhpModuleCustomizer> createProviderPanels() {
         Map<PhpDocumentationProvider, PhpModuleCustomizer> panels = new ConcurrentHashMap<>();
         for (PhpDocumentationProvider provider : PhpDocumentations.getDocumentations()) {
         for (PhpDocumentationProvider provider : getProviders()) {
             providerComboBox.addItem(provider);
         }
+        if (originalProvider != null) {
+            providerComboBox.setSelectedItem(originalProvider);
+        }
         providerComboBox.setRenderer(new PhpDocumentationProviderRenderer());
         // listeners
-        for (PhpModuleCustomizer customizer : getCustomizers()) {
-            customizer.addChangeListener(this);
-        }
         providerComboBox.addActionListener(new ProviderActionListener());
         category.setStoreListener(new ActionListener() {
             @Override
 
     void providerChanged() {
         assert EventQueue.isDispatchThread();
+        removeListener();
+        selectedProvider = getSelectedProvider();
         // switch panel
         providerPanel.removeAll();
         PhpModuleCustomizer selectedPanel = getSelectedPanel();
         if (selectedPanel != null) {
             providerPanel.add(selectedPanel.getComponent(), BorderLayout.CENTER);
+            selectedPanel.addChangeListener(this);
         }
         providerPanel.revalidate();
         providerPanel.repaint();
         validateData();
     }
 
-    @NbBundle.Messages({
-        "# {0} - provider name",
-        "# {1} - message",
-        "CustomizerDocumentation.message={0}: {1}",
-    })
     void validateData() {
         assert EventQueue.isDispatchThread();
-        String warning = null;
-        for (PhpModuleCustomizer customizer : getCustomizers()) {
-            String msg = customizer.getErrorMessage();
-            if (msg != null) {
-                category.setErrorMessage(Bundle.CustomizerDocumentation_message(customizer.getDisplayName(), msg));
+        PhpModuleCustomizer customizer = getSelectedPanel();
+        if (customizer != null) {
+            String error = customizer.getErrorMessage();
+            if (error != null) {
+                category.setErrorMessage(error);
                 category.setValid(false);
                 return;
             }
-            msg = customizer.getWarningMessage();
-            if (warning == null
-                    && msg != null) {
-                warning = Bundle.CustomizerDocumentation_message(customizer.getDisplayName(), msg);
+            String warning = customizer.getWarningMessage();
+            if (warning != null) {
+                category.setErrorMessage(warning);
+                category.setValid(true);
+                return;
             }
         }
-        // warning
-        category.setErrorMessage(warning);
+        // all ok
+        category.setErrorMessage(null);
         category.setValid(true);
     }
 
     void storeData() {
         assert !EventQueue.isDispatchThread();
+        if (originalProvider != selectedProvider) {
+            if (originalProvider != null) {
+                originalProvider.notifyEnabled(phpModule, false);
+            }
+            if (selectedProvider != null) {
+                selectedProvider.notifyEnabled(phpModule, true);
+            }
+        }
         for (PhpModuleCustomizer customizer : getCustomizers()) {
             customizer.save();
         }
     }
 
     void cleanup() {
+        removeListener();
+        for (PhpModuleCustomizer customizer : getCustomizers()) {
+            customizer.close();
+        }
+    }
+
+    private void removeListener() {
         for (PhpModuleCustomizer customizer : getCustomizers()) {
             customizer.removeChangeListener(this);
-            customizer.close();
         }
     }
 
     }
 
     @CheckForNull
+    private PhpDocumentationProvider getSelectedProvider() {
+        return (PhpDocumentationProvider) providerComboBox.getSelectedItem();
+    }
+
+    @CheckForNull
     private PhpModuleCustomizer getSelectedPanel() {
         assert EventQueue.isDispatchThread();
         assert providerPanels != null;
-        return providerPanels.get((PhpDocumentationProvider) providerComboBox.getSelectedItem());
+        PhpDocumentationProvider selecteProvider = getSelectedProvider();
+        if (selecteProvider == null) {
+            // #246324
+            return null;
+        }
+        return providerPanels.get(selecteProvider);
     }
 
     private List<PhpDocumentationProvider> getProviders() {

php.api.documentation/src/org/netbeans/modules/php/spi/documentation/PhpDocumentationProvider.java

      *
      * @param  phpModule the PHP module; never <code>null</code>
      * @return <code>true</code> if the PHP module already contains documentation for this PHP documentation provider, <code>false</code> otherwise.
+     * @see #notifyEnabled(PhpModule, boolean)
      */
     public boolean isInPhpModule(@NonNull PhpModule phpModule) {
         return true;
     public abstract void generateDocumentation(@NonNull PhpModule phpModule);
 
     /**
+     * Notify when this provider is enabled or disabled. Typically, provider
+     * stores the state in its properties and returns it when {@link #isInPhpModule(PhpModule)}
+     * is called.
+     * @param phpModule PHP module, never {@code null}
+     * @param enabled {@code true} if enabled, {@code false} otherwise
+     * @see #isInPhpModule(PhpModule)
+     * @since 0.12
+     */
+    public void notifyEnabled(@NonNull PhpModule phpModule, boolean enabled) {
+        // noop
+    }
+
+    /**
      * Declarative registration of a singleton PHP documentation provider provider.
      * By marking an implementation class or a factory method with this annotation,
      * you automatically register that implementation, normally in {@link org.netbeans.modules.php.api.documentation.PhpDocs#DOCS_PATH}.

php.api.phpmodule/manifest.mf

 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.modules.php.api.phpmodule
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/api/phpmodule/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.44
+OpenIDE-Module-Specification-Version: 2.45

php.api.phpmodule/src/org/netbeans/modules/php/api/util/UiUtils.java

 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.api.options.OptionsDisplayer;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ui.ProjectProblems;
 import org.netbeans.modules.php.api.phpmodule.PhpModule;
 import org.netbeans.modules.php.api.ui.SearchPanel;
 import org.netbeans.spi.project.ui.CustomizerProvider2;
         return base;
     }
 
+    /**
+     * Informs user about broken PHP module.
+     * <p>
+     * This method shows a dialog with possibility to open Project Problems dialog.
+     * @param phpModule broken PHP module
+     * @since 2.45
+     */
+    @NbBundle.Messages({
+        "# {0} - project name",
+        "UiUtils.metadata.corrupted=<html><b>Project {0} is corrupted.</b><br><br>Do you want to open Project Problems dialog?"
+    })
+    public static void warnBrokenProject(PhpModule phpModule) {
+        Parameters.notNull("phpModule", phpModule); // NOI18N
+        assert phpModule.isBroken() : "Not broken php module " + phpModule.getName();
+        String name = phpModule.getDisplayName();
+        NotifyDescriptor descriptor = new NotifyDescriptor.Confirmation(
+                Bundle.UiUtils_metadata_corrupted(name),
+                name,
+                NotifyDescriptor.YES_NO_OPTION,
+                NotifyDescriptor.WARNING_MESSAGE);
+        if (DialogDisplayer.getDefault().notify(descriptor) == NotifyDescriptor.YES_OPTION) {
+            Project project = FileOwnerQuery.getOwner(phpModule.getProjectDirectory());
+            assert project != null : "Must found project for " + phpModule.getProjectDirectory();
+            ProjectProblems.showCustomizer(project);
+        }
+    }
+
     static NotifyDescriptor createNotifyDescriptor(ExecutionException exc) {
         assert exc != null;
         final Throwable cause = exc.getCause();

php.apigen/manifest.mf

 OpenIDE-Module: org.netbeans.modules.php.apigen
 OpenIDE-Module-Layer: org/netbeans/modules/php/apigen/resources/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/apigen/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.25
+OpenIDE-Module-Specification-Version: 1.26

php.apigen/nbproject/project.xml

                     <compile-dependency/>
                     <run-dependency>
                         <release-version>0</release-version>
-                        <specification-version>0.11</specification-version>
+                        <specification-version>0.12</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>

php.apigen/src/org/netbeans/modules/php/apigen/ApiGenProvider.java

 import org.netbeans.modules.php.api.phpmodule.PhpModule;
 import org.netbeans.modules.php.api.util.UiUtils;
 import org.netbeans.modules.php.apigen.commands.ApiGenScript;
+import org.netbeans.modules.php.apigen.ui.ApiGenPreferences;
 import org.netbeans.modules.php.apigen.ui.customizer.PhpModuleCustomizerImpl;
 import org.netbeans.modules.php.apigen.ui.options.ApiGenOptionsPanelController;
 import org.netbeans.modules.php.spi.documentation.PhpDocumentationProvider;
     }
 
     @Override
+    public boolean isInPhpModule(PhpModule phpModule) {
+        return ApiGenPreferences.isEnabled(phpModule);
+    }
+
+    @Override
     public PhpModuleCustomizer createPhpModuleCustomizer(PhpModule phpModule) {
         return new PhpModuleCustomizerImpl(phpModule);
     }
         }
     }
 
+    @Override
+    public void notifyEnabled(PhpModule phpModule, boolean enabled) {
+        ApiGenPreferences.setEnabled(phpModule, enabled);
+    }
+
 }

php.apigen/src/org/netbeans/modules/php/apigen/ui/ApiGenPreferences.java

     // package private
     static final Property<Object> TARGET = new Property<>("target"); // NOI18N
 
+    private static final String ENABLED = "enabled"; // NOI18N
     private static final String DEFAULT_VALUE = ""; // NOI18N
     private static final String SEPARATOR = ","; // NOI18N
 
     private ApiGenPreferences() {
     }
 
+    public static boolean isEnabled(PhpModule phpModule) {
+        return getPreferences(phpModule).getBoolean(ENABLED, false);
+    }
+
+    public static void setEnabled(PhpModule phpModule, boolean enabled) {
+        getPreferences(phpModule).putBoolean(ENABLED, enabled);
+    }
+
     public static String getTarget(PhpModule phpModule, boolean showPanel) {
         String target = get(phpModule, TARGET);
         if (StringUtils.isEmpty(target) && showPanel) {

php.editor/src/org/netbeans/modules/php/editor/verification/PHPHintsProvider.java

 
 package org.netbeans.modules.php.editor.verification;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
                     adjustAndInvoke(rule, adjuster);
                 }
             }
-            boolean checkResult = false;
-            assert checkResult = true;
-            if (checkResult) {
-                for (T item : result) {
-                    assert item != null : item;
-                }
-            }
         }
 
         private void adjustAndInvoke(Rule rule, RuleAdjuster adjuster) {
                 if (cancel) {
                     return;
                 }
-                invokableRule.invoke(ruleContext, result);
+                List<T> tempResult = new ArrayList<>();
+                invokableRule.invoke(ruleContext, tempResult);
+                boolean checkResult = false;
+                assert checkResult = true;
+                if (checkResult) {
+                    for (T item : tempResult) {
+                        assert item != null : rule;
+                    }
+                }
+                result.addAll(tempResult);
             }
         }
 

php.phpdoc/manifest.mf

 OpenIDE-Module: org.netbeans.modules.php.phpdoc
 OpenIDE-Module-Layer: org/netbeans/modules/php/phpdoc/resources/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/phpdoc/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.19
+OpenIDE-Module-Specification-Version: 1.20
 

php.phpdoc/nbproject/project.xml

                     <compile-dependency/>
                     <run-dependency>
                         <release-version>0</release-version>
-                        <specification-version>0.11</specification-version>
+                        <specification-version>0.12</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>

php.phpdoc/src/org/netbeans/modules/php/phpdoc/PhpDocumentorProvider.java

 import org.netbeans.modules.php.api.executable.InvalidPhpExecutableException;
 import org.netbeans.modules.php.api.phpmodule.PhpModule;
 import org.netbeans.modules.php.api.util.UiUtils;
+import org.netbeans.modules.php.phpdoc.ui.PhpDocPreferences;
 import org.netbeans.modules.php.phpdoc.ui.customizer.PhpModuleCustomizerImpl;
 import org.netbeans.modules.php.spi.documentation.PhpDocumentationProvider;
 import org.netbeans.modules.php.spi.phpmodule.PhpModuleCustomizer;
     }
 
     @Override
+    public boolean isInPhpModule(PhpModule phpModule) {
+        return PhpDocPreferences.isEnabled(phpModule);
+    }
+
+    @Override
     public PhpModuleCustomizer createPhpModuleCustomizer(PhpModule phpModule) {
         return new PhpModuleCustomizerImpl(phpModule);
     }
             UiUtils.invalidScriptProvided(ex.getLocalizedMessage(), PhpDocScript.OPTIONS_SUB_PATH);
         }
     }
+
+    @Override
+    public void notifyEnabled(PhpModule phpModule, boolean enabled) {
+        PhpDocPreferences.setEnabled(phpModule, enabled);
+    }
+
 }

php.phpdoc/src/org/netbeans/modules/php/phpdoc/ui/PhpDocPreferences.java

 import org.netbeans.modules.php.api.phpmodule.PhpModule;
 
 public final class PhpDocPreferences {
+
+    private static final String PHPDOC_ENABLED = "enabled"; // NOI18N
     private static final String PHPDOC_TARGET = "target"; // NOI18N
     private static final String PHPDOC_TITLE = "title"; // NOI18N
 
+
     private PhpDocPreferences() {
     }
 
+    public static boolean isEnabled(PhpModule phpModule) {
+        return getPreferences(phpModule).getBoolean(PHPDOC_ENABLED, false);
+    }
+
+    public static void setEnabled(PhpModule phpModule, boolean enabled) {
+        getPreferences(phpModule).putBoolean(PHPDOC_ENABLED, enabled);
+    }
+
     public static String getPhpDocTarget(PhpModule phpModule, boolean showPanel) {
         Preferences preferences = getPreferences(phpModule);
         String phpDocTarget = preferences.get(PHPDOC_TARGET, null);

php.phpdoc/src/org/netbeans/modules/php/phpdoc/ui/options/Bundle.properties

 # {0} - short script name
 # {1} - long script name
 # {2} - PHAR script name
-LBL_PhpDocUsage=Full path of phpDocumentor script (typically {0}, {1} or {2}).
+LBL_PhpDocUsage=Full path of script (typically {0}, {1} or {2}).
 PhpDocOptionsPanel.browseButton.text=&Browse...
 PhpDocOptionsPanel.searchButton.text=S&earch...
 PhpDocOptionsPanel.installationInfoLabel.text=Installation instructions can be found on the phpDocumentor web site.
 PhpDocOptionsPanel.phpDocLabel.text=&phpDocumentor script:
 PhpDocOptionsPanel.installationLearnMoreLabel.text=<html><a href="#">Learn more</a></html>
+PhpDocOptionsPanel.versionInfoLabel.text=phpDocumentor 2 is supported.

php.phpdoc/src/org/netbeans/modules/php/phpdoc/ui/options/PhpDocOptionsPanel.form

   <Layout>
     <DimensionLayout dim="0">
       <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" attributes="0">
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
-                  <Component id="installationInfoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
-                  <Component id="installationLearnMoreLabel" alignment="0" min="-2" max="-2" attributes="0"/>
-              </Group>
-              <EmptySpace min="-2" max="-2" attributes="0"/>
-          </Group>
           <Group type="102" alignment="0" attributes="0">
               <Component id="phpDocLabel" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
                   </Group>
               </Group>
           </Group>
+          <Group type="102" attributes="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Component id="installationInfoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Component id="installationLearnMoreLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Component id="versionInfoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+          </Group>
       </Group>
     </DimensionLayout>
     <DimensionLayout dim="1">
               </Group>
               <EmptySpace max="-2" attributes="0"/>
               <Component id="phpDocUsageLabel" min="-2" max="-2" attributes="0"/>
-              <EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
+              <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
+              <Component id="versionInfoLabel" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
               <Component id="installationInfoLabel" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
               <Component id="installationLearnMoreLabel" min="-2" max="-2" attributes="0"/>
         <Property name="text" type="java.lang.String" value="ERROR" noResource="true"/>
       </Properties>
     </Component>
+    <Component class="javax.swing.JLabel" name="versionInfoLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/php/phpdoc/ui/options/Bundle.properties" key="PhpDocOptionsPanel.versionInfoLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
   </SubComponents>
 </Form>

php.phpdoc/src/org/netbeans/modules/php/phpdoc/ui/options/PhpDocOptionsPanel.java

         installationInfoLabel = new JLabel();
         installationLearnMoreLabel = new JLabel();
         errorLabel = new JLabel();
+        versionInfoLabel = new JLabel();
+
         Mnemonics.setLocalizedText(phpDocLabel, NbBundle.getMessage(PhpDocOptionsPanel.class, "PhpDocOptionsPanel.phpDocLabel.text")); // NOI18N
+
         Mnemonics.setLocalizedText(browseButton, NbBundle.getMessage(PhpDocOptionsPanel.class, "PhpDocOptionsPanel.browseButton.text")); // NOI18N
         browseButton.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent evt) {
                 browseButtonActionPerformed(evt);
             }
         });
+
         Mnemonics.setLocalizedText(searchButton, NbBundle.getMessage(PhpDocOptionsPanel.class, "PhpDocOptionsPanel.searchButton.text")); // NOI18N
         searchButton.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent evt) {
                 searchButtonActionPerformed(evt);
             }
         });
+
         Mnemonics.setLocalizedText(phpDocUsageLabel, "HINT"); // NOI18N
+
         Mnemonics.setLocalizedText(installationInfoLabel, NbBundle.getMessage(PhpDocOptionsPanel.class, "PhpDocOptionsPanel.installationInfoLabel.text")); // NOI18N
+
         Mnemonics.setLocalizedText(installationLearnMoreLabel, NbBundle.getMessage(PhpDocOptionsPanel.class, "PhpDocOptionsPanel.installationLearnMoreLabel.text")); // NOI18N
         installationLearnMoreLabel.addMouseListener(new MouseAdapter() {
             public void mouseEntered(MouseEvent evt) {
                 installationLearnMoreLabelMousePressed(evt);
             }
         });
+
         Mnemonics.setLocalizedText(errorLabel, "ERROR"); // NOI18N
 
+        Mnemonics.setLocalizedText(versionInfoLabel, NbBundle.getMessage(PhpDocOptionsPanel.class, "PhpDocOptionsPanel.versionInfoLabel.text")); // NOI18N
+
         GroupLayout layout = new GroupLayout(this);
         this.setLayout(layout);
         layout.setHorizontalGroup(
-            layout.createParallelGroup(Alignment.LEADING).addGroup(layout.createSequentialGroup()
-
-                .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(errorLabel).addComponent(installationInfoLabel).addComponent(installationLearnMoreLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)).addContainerGap()).addGroup(layout.createSequentialGroup()
+            layout.createParallelGroup(Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
                 .addComponent(phpDocLabel)
-
-                .addPreferredGap(ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(Alignment.LEADING).addGroup(layout.createSequentialGroup()
+                .addPreferredGap(ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(Alignment.LEADING)
+                    .addGroup(layout.createSequentialGroup()
                         .addComponent(phpDocUsageLabel)
-                        .addContainerGap()).addGroup(layout.createSequentialGroup()
+                        .addContainerGap())
+                    .addGroup(layout.createSequentialGroup()
                         .addComponent(phpDocTextField)
-
-                        .addPreferredGap(ComponentPlacement.RELATED).addComponent(browseButton).addPreferredGap(ComponentPlacement.RELATED).addComponent(searchButton))))
+                        .addPreferredGap(ComponentPlacement.RELATED)
+                        .addComponent(browseButton)
+                        .addPreferredGap(ComponentPlacement.RELATED)
+                        .addComponent(searchButton))))
+            .addGroup(layout.createSequentialGroup()
+                .addGroup(layout.createParallelGroup(Alignment.LEADING)
+                    .addComponent(errorLabel)
+                    .addComponent(installationInfoLabel)
+                    .addComponent(installationLearnMoreLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(versionInfoLabel))
+                .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
         layout.setVerticalGroup(
-            layout.createParallelGroup(Alignment.LEADING).addGroup(layout.createSequentialGroup()
-
-                .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(phpDocLabel).addComponent(phpDocTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE).addComponent(searchButton).addComponent(browseButton)).addPreferredGap(ComponentPlacement.RELATED).addComponent(phpDocUsageLabel).addGap(18, 18, 18).addComponent(installationInfoLabel).addPreferredGap(ComponentPlacement.RELATED).addComponent(installationLearnMoreLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE).addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(errorLabel))
+            layout.createParallelGroup(Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+                    .addComponent(phpDocLabel)
+                    .addComponent(phpDocTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(searchButton)
+                    .addComponent(browseButton))
+                .addPreferredGap(ComponentPlacement.RELATED)
+                .addComponent(phpDocUsageLabel)
+                .addGap(18, 18, 18)
+                .addComponent(versionInfoLabel)
+                .addPreferredGap(ComponentPlacement.RELATED)
+                .addComponent(installationInfoLabel)
+                .addPreferredGap(ComponentPlacement.RELATED)
+                .addComponent(installationLearnMoreLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(errorLabel))
         );
     }// </editor-fold>//GEN-END:initComponents
 
     private JTextField phpDocTextField;
     private JLabel phpDocUsageLabel;
     private JButton searchButton;
+    private JLabel versionInfoLabel;
     // End of variables declaration//GEN-END:variables
 
 }

php.project/manifest.mf

 Manifest-Version: 1.0
 AutoUpdate-Show-In-Client: false
-OpenIDE-Module-Specification-Version: 2.117
+OpenIDE-Module-Specification-Version: 2.118
 OpenIDE-Module: org.netbeans.modules.php.project
 OpenIDE-Module-Layer: org/netbeans/modules/php/project/resources/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/project/resources/Bundle.properties

php.project/nbproject/project.xml

                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>2.41</specification-version>
+                        <specification-version>2.45</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>

php.project/src/org/netbeans/modules/php/project/ui/Utils.java

 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.api.annotations.common.StaticResource;
 import org.netbeans.api.project.ProjectUtils;
-import org.netbeans.api.project.ui.ProjectProblems;
 import org.netbeans.modules.php.api.PhpVersion;
 import org.netbeans.modules.php.api.util.FileUtils;
 import org.netbeans.modules.php.api.util.StringUtils;
 import org.netbeans.modules.php.project.ProjectPropertiesSupport;
 import org.netbeans.modules.php.project.ui.options.PhpOptionsPanelController;
 import org.netbeans.spi.project.support.ant.PropertyUtils;
-import org.openide.DialogDisplayer;
-import org.openide.NotifyDescriptor;
 import org.openide.filesystems.FileChooserBuilder;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
     private Utils() {
     }
 
-    @NbBundle.Messages({
-        "# {0} - project name",
-        "Utils.metadata.corrupted=<html><b>Project {0} is corrupted.</b><br><br>Do you want to open Project Problems dialog?"
-    })
-    public static void warnInvalidSourcesDirectory(PhpProject project) {
-        String name = project.getName();
-        NotifyDescriptor descriptor = new NotifyDescriptor.Confirmation(
-                Bundle.Utils_metadata_corrupted(name),
-                name,
-                NotifyDescriptor.YES_NO_OPTION,
-                NotifyDescriptor.WARNING_MESSAGE);
-        if (DialogDisplayer.getDefault().notify(descriptor) == NotifyDescriptor.YES_OPTION) {
-            ProjectProblems.showCustomizer(project);
-        }
-    }
-
     // XXX use everywhere
     @NonNull
     public static Color getErrorForeground() {

php.project/src/org/netbeans/modules/php/project/ui/actions/Command.java

 
 import java.util.logging.Logger;
 import org.netbeans.modules.php.api.phpmodule.PhpModule;
+import org.netbeans.modules.php.api.util.UiUtils;
 import org.netbeans.modules.php.project.PhpProject;
 import org.netbeans.modules.php.project.PhpProjectValidator;
 import org.netbeans.modules.php.project.ProjectPropertiesSupport;
-import org.netbeans.modules.php.project.ui.Utils;
 import org.netbeans.modules.php.project.ui.actions.support.CommandUtils;
 import org.netbeans.modules.php.project.ui.actions.support.ConfigAction;
 import org.netbeans.modules.php.spi.testing.PhpTestingProvider;
 
     protected boolean validateInvokeAction(Lookup context) {
         if (PhpProjectValidator.isFatallyBroken(project)) {
-            Utils.warnInvalidSourcesDirectory(project);
+            UiUtils.warnBrokenProject(project.getPhpModule());
             return false;
         }
         return true;

php.project/src/org/netbeans/modules/php/project/ui/actions/tests/GoToTest.java

 import java.util.logging.Logger;
 import org.netbeans.modules.php.api.phpmodule.PhpModule;
 import org.netbeans.modules.php.api.util.FileUtils;
+import org.netbeans.modules.php.api.util.UiUtils;
 import org.netbeans.modules.php.project.PhpProject;
 import org.netbeans.modules.php.project.PhpProjectValidator;
 import org.netbeans.modules.php.project.ProjectPropertiesSupport;
 import org.netbeans.modules.php.project.ui.actions.support.CommandUtils;
-import org.netbeans.modules.php.project.ui.Utils;
 import org.netbeans.modules.php.project.ui.customizer.CompositePanelProviderImpl;
 import org.netbeans.modules.php.project.util.PhpProjectUtils;
 import org.netbeans.modules.php.spi.testing.locate.Locations;
             return null;
         }
         if (PhpProjectValidator.isFatallyBroken(project)) {
-            Utils.warnInvalidSourcesDirectory(project);
+            UiUtils.warnBrokenProject(project.getPhpModule());
             return null;
         }
 

php.project/src/org/netbeans/modules/php/project/ui/customizer/CustomizerProviderImpl.java

 import java.util.Map;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.modules.php.api.util.UiUtils;
 import org.netbeans.modules.php.project.PhpProject;
 import org.netbeans.modules.php.project.PhpProjectValidator;
 import org.netbeans.modules.php.project.ProjectPropertiesSupport;
 import org.netbeans.modules.php.project.classpath.IncludePathSupport;
-import org.netbeans.modules.php.project.ui.Utils;
 import org.netbeans.spi.project.ui.CustomizerProvider2;
 import org.netbeans.spi.project.ui.support.ProjectCustomizer;
 import org.openide.util.Lookup;
     public void showCustomizer(final String preselectedCategory, String preselectedSubCategory) {
         if (PhpProjectValidator.isFatallyBroken(project)) {
             // metadata corrupted
-            Utils.warnInvalidSourcesDirectory(project);
+            UiUtils.warnBrokenProject(project.getPhpModule());
             return;
         }
         Mutex.EVENT.readAccess(new Runnable() {

php.project/src/org/netbeans/modules/php/project/ui/logicalview/Bundle.properties

 # 0 - file path
 # 1 - framework name
 LBL_ImportantFileTooltip={0} - {1}
-PhpDoc.action.generate.label=Generate Documentation

php.project/src/org/netbeans/modules/php/project/ui/logicalview/PhpLogicalViewProvider.java

 import java.io.CharConversionException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.logging.Level;
 import org.netbeans.api.project.ProjectInformation;
 import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.modules.gsf.codecoverage.api.CoverageActionFactory;
-import org.netbeans.modules.php.api.documentation.PhpDocumentations;
 import org.netbeans.modules.php.api.framework.BadgeIcon;
 import org.netbeans.modules.php.api.phpmodule.PhpModule;
 import org.netbeans.modules.php.project.PhpProject;
-import org.netbeans.modules.php.project.PhpProjectValidator;
 import org.netbeans.modules.php.project.ProjectPropertiesSupport;
 import org.netbeans.modules.php.project.ui.Utils;
 import org.netbeans.modules.php.project.util.PhpProjectUtils;
-import org.netbeans.modules.php.spi.documentation.PhpDocumentationProvider;
 import org.netbeans.modules.php.spi.framework.PhpFrameworkProvider;
 import org.netbeans.modules.php.spi.framework.PhpModuleActionsExtender;
 import org.netbeans.modules.php.spi.framework.actions.RunCommandAction;
 import org.netbeans.spi.project.ui.support.CommonProjectActions;
 import org.netbeans.spi.project.ui.support.NodeFactory;
 import org.netbeans.spi.project.ui.support.NodeFactorySupport;
-import org.openide.LifecycleManager;
 import org.openide.awt.ActionID;
 import org.openide.awt.ActionReference;
 import org.openide.awt.ActionReferences;
-import org.openide.awt.ActionRegistration;
 import org.openide.awt.Actions;
-import org.openide.awt.DynamicMenuContent;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.loaders.DataObject;
 import org.openide.nodes.Node;
 import org.openide.nodes.NodeNotFoundException;
 import org.openide.nodes.NodeOp;
-import org.openide.util.ContextAwareAction;
 import org.openide.util.HelpCtx;
 import org.openide.util.ImageUtilities;
 import org.openide.util.Lookup;
 
         private void addFrameworks(List<Action> actions) {
             // find index
-            int documentationIndex = actions.size();
+            int positionIndex = actions.size();
             for (int i = 0; i < actions.size(); i++) {
                 Action action = actions.get(i);
-                if (action instanceof DocumentationActionFactory) {
-                    documentationIndex = i;
+                if (action == null) {
+                    continue;
+                }
+                if ("org.netbeans.modules.php.composer.ui.actions.ComposerActionsFactory".equals(action.getClass().getName())) { // NOI18N
+                    positionIndex = i;
                     break;
                 }
             }
                         allActions.addAll(frameworkActions);
                         if (!separatorAdded) {
                             separatorAdded = true;
-                            actions.add(++documentationIndex, null);
+                            actions.add(++positionIndex, null);
                         }
-                        actions.add(++documentationIndex, new FrameworkMenu(actionsExtender.getMenuName(), allActions));
+                        actions.add(++positionIndex, new FrameworkMenu(actionsExtender.getMenuName(), allActions));
                     }
                 }
             }
             if (separatorAdded) {
-                actions.add(++documentationIndex, null);
+                actions.add(++positionIndex, null);
             }
         }
 
         }
     }
 
-    /**
-     * Add 'Generate documentation' menu (for 1 provider) or submenu (for more providers).
-     * Do nothing if there are no providers.
-     */
-    @ActionID(id="org.netbeans.modules.php.project.ui.logicalview.PhpLogicalViewProvider$DocumentationActionFactory", category="Project")
-    @ActionRegistration(displayName="#PhpDoc.action.generate.label", lazy=false)
-    @ActionReference(position=800, path="Projects/org-netbeans-modules-php-phpproject/Actions")
-    public static final class DocumentationActionFactory extends AbstractAction implements ContextAwareAction {
-
-        private static final long serialVersionUID = 5687856454545L;
-
-
-        public DocumentationActionFactory() {
-            setEnabled(false);
-            putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            assert false;
-        }
-
-        @Override
-        public Action createContextAwareInstance(Lookup actionContext) {
-            Collection<? extends Project> projects = actionContext.lookupAll(Project.class);
-            if (projects.size() != 1) {
-                return this;
-            }
-            PhpProject phpProject = projects.iterator().next().getLookup().lookup(PhpProject.class);
-            if (phpProject == null) {
-                return this;
-            }
-            List<PhpDocumentationProvider> docProviders = PhpDocumentations.getDocumentations();
-            if (docProviders.isEmpty()) {
-                return this;
-            }
-
-            PhpModule phpModule = phpProject.getPhpModule();
-            List<PhpDocumentationProvider> projectDocProviders = new ArrayList<>(docProviders.size());
-            for (PhpDocumentationProvider docProvider : docProviders) {
-                if (docProvider.isInPhpModule(phpModule)) {
-                    projectDocProviders.add(docProvider);
-                }
-            }
-            if (projectDocProviders.isEmpty()) {
-                return this;
-            }
-            if (projectDocProviders.size() == 1) {
-                return new PhpDocAction(phpProject, projectDocProviders.get(0));
-            }
-            return new DocumentationMenu(phpProject, projectDocProviders);
-        }
-
-    }
-
-    private static class DocumentationMenu extends AbstractAction implements Presenter.Popup {
-
-        private static final long serialVersionUID = 1587896543546879L;
-
-        private final PhpProject phpProject;
-        private final List<PhpDocumentationProvider> docProviders;
-
-        public DocumentationMenu(PhpProject phpProject, List<PhpDocumentationProvider> docProviders) {
-            super(NbBundle.getMessage(DocumentationMenu.class, "PhpDoc.action.generate.label"), null);
-            assert phpProject != null;
-            assert docProviders != null;
-
-            putValue(SHORT_DESCRIPTION, NbBundle.getMessage(DocumentationMenu.class, "PhpDoc.action.generate.label"));
-            this.phpProject = phpProject;
-            this.docProviders = docProviders;
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            assert false;
-        }
-
-        @Override
-        public JMenuItem getPopupPresenter() {
-            List<PhpDocAction> docActions = new ArrayList<>(docProviders.size());
-            for (PhpDocumentationProvider docProvider : docProviders) {
-                docActions.add(new PhpDocAction(docProvider.getDisplayName(), phpProject, docProvider));
-            }
-            return new DocumentationSubMenu(docActions);
-        }
-    }
-
-    private static class DocumentationSubMenu extends BaseSubMenu {
-
-        private static final long serialVersionUID = -6764324657641L;
-
-
-        public DocumentationSubMenu(List<PhpDocAction> docActions) {
-            super(NbBundle.getMessage(DocumentationSubMenu.class, "PhpDoc.action.generate.label"));
-
-            for (PhpDocAction action : docActions) {
-                add(toMenuItem(action));
-            }
-        }
-
-    }
-
-    private static final class PhpDocAction extends AbstractAction {
-
-        private static final long serialVersionUID = 178423135454L;
-
-        private static final RequestProcessor RP = new RequestProcessor("Generating php documentation", 2); // NOI18N
-
-        private final PhpProject phpProject;
-        private final PhpDocumentationProvider docProvider;
-
-
-        public PhpDocAction(PhpProject phpProject, PhpDocumentationProvider docProvider) {
-            this(NbBundle.getMessage(PhpDocAction.class, "PhpDoc.action.generate.label"), phpProject, docProvider);
-        }
-
-        public PhpDocAction(String name, PhpProject phpProject, PhpDocumentationProvider docProvider) {