/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2015 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 2015 Sun Microsystems, Inc. */ package org.netbeans.modules.php.cake3.editor; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.modules.csl.spi.ParserResult; import org.netbeans.modules.parsing.api.ParserManager; import org.netbeans.modules.parsing.api.ResultIterator; import org.netbeans.modules.parsing.api.Source; import org.netbeans.modules.parsing.api.UserTask; import org.netbeans.modules.parsing.spi.ParseException; import org.netbeans.modules.php.api.editor.PhpBaseElement; import org.netbeans.modules.php.api.editor.PhpClass; import org.netbeans.modules.php.api.editor.PhpVariable; import org.netbeans.modules.php.api.phpmodule.PhpModule; import org.netbeans.modules.php.cake3.editor.visitors.ControllerVisitor; import org.netbeans.modules.php.cake3.editor.visitors.HelperVisitor; import org.netbeans.modules.php.cake3.editor.visitors.ViewVisitor; import org.netbeans.modules.php.cake3.modules.CakePHP3Module; import org.netbeans.modules.php.cake3.modules.CakePHP3Module.Base; import org.netbeans.modules.php.cake3.modules.CakePHP3Module.Category; import org.netbeans.modules.php.cake3.modules.ModuleUtils; import org.netbeans.modules.php.editor.parser.PHPParseResult; import org.netbeans.modules.php.editor.parser.api.Utils; import org.netbeans.modules.php.spi.editor.EditorExtender; import org.openide.filesystems.FileObject; import org.openide.util.Pair; /** * * @author junichi11 */ public class CakePHP3EditorExtender extends EditorExtender { private final PhpModule phpModule; private Category category = Category.UNKNOWN; private static final Logger LOGGER = Logger.getLogger(CakePHP3EditorExtender.class.getName()); public CakePHP3EditorExtender(PhpModule phpModule) { this.phpModule = phpModule; } @Override public List<PhpBaseElement> getElementsForCodeCompletion(FileObject fo) { // Enabled PhpBaseElement is just PhpVariable CakePHP3Module cakeModule = CakePHP3Module.forPhpModule(phpModule); String ext = fo.getExt(); if (!ext.equals("php") && !ext.equals(cakeModule.getCtpExt())) { // NOI18N return Collections.emptyList(); } if (cakeModule.isTemplateFile(fo)) { category = Category.TEMPLATE; } else { category = cakeModule.getCategory(fo); } if (!isEnabledCategory(category)) { return Collections.emptyList(); } List<PhpBaseElement> elements = new ArrayList<>(); Base base = cakeModule.getBase(fo); if (base == Base.UNKNOWN) { return elements; } // plugin String pluginName = null; if (base == Base.PLUGIN) { pluginName = cakeModule.getPluginName(fo); } // XXX should not be parsed parseAppController(cakeModule, base, pluginName, elements); parseAppView(cakeModule, base, pluginName, elements); parseCurrentFile(fo, elements); return elements; } private void parseAppController(CakePHP3Module cakeModule, Base base, String pluginName, List<PhpBaseElement> elements) { FileObject appController = cakeModule.getFile(base, Category.CONTROLLER, "AppController.php", pluginName); // NOI18N if (appController != null) { Set<PhpClass> phpClasses = parseFields(appController, Category.CONTROLLER); for (PhpClass phpClass : phpClasses) { elements.add(new PhpVariable("$this", phpClass, null, 0)); // NOI18N } } } private void parseAppView(CakePHP3Module cakeModule, Base base, String pluginName, List<PhpBaseElement> elements) { if (ModuleUtils.isTemplate(category)) { FileObject appView = cakeModule.getFile(base, Category.VIEW, "AppView.php", pluginName); // NOI18N if (appView != null) { Set<PhpClass> phpClasses = parseView(appView); for (PhpClass phpClass : phpClasses) { elements.add(new PhpVariable("$this", phpClass, null, 0)); // NOI18N } } } } private void parseCurrentFile(FileObject fo, List<PhpBaseElement> elements) { Set<PhpClass> phpClasses = parseFields(fo, category); for (PhpClass phpClass : phpClasses) { elements.add(new PhpVariable("$this", phpClass)); // NOI18N } } private Set<PhpClass> parseFields(FileObject fo, final Category category) { FileObject target = fo; PhpClass phpClass = getPhpClass(); if (phpClass == null) { return Collections.emptySet(); } String fileName = fo.getName(); // if category is template, get a controller file for a template file if (!"AppController".equals(fileName) && category == Category.TEMPLATE) { CakePHP3Module cakeModule = CakePHP3Module.forPhpModule(phpModule); target = cakeModule.getController(fo); if (target == null) { return Collections.singleton(phpClass); } } final Set<Pair<String, PhpClass>> phpClasses = new HashSet<>(); try { ParserManager.parse(Collections.singleton(Source.create(target)), new UserTask() { @Override public void run(ResultIterator resultIterator) throws Exception { PHPParseResult result = (PHPParseResult) resultIterator.getParserResult(); if (result == null) { return; } if (category == Category.CONTROLLER) { ControllerVisitor controllerVisitor = new ControllerVisitor(phpModule); controllerVisitor.scan(Utils.getRoot(result)); phpClasses.addAll(controllerVisitor.getPhpClasses()); } else if (ModuleUtils.isTemplate(category)) { ControllerVisitor controllerVisitor = new ControllerVisitor(phpModule, true); controllerVisitor.scan(Utils.getRoot(result)); phpClasses.addAll(controllerVisitor.getPhpClasses()); } else if (category == Category.HELPER) { HelperVisitor helperVisitor = new HelperVisitor(phpModule); helperVisitor.scan(Utils.getRoot(result)); phpClasses.addAll(helperVisitor.getPhpClasses()); } } }); } catch (ParseException ex) { LOGGER.log(Level.WARNING, null, ex); } for (Pair<String, PhpClass> clazz : phpClasses) { phpClass.addField(clazz.first(), clazz.second(), clazz.second().getFile(), 0); } return Collections.singleton(phpClass); } private Set<PhpClass> parseView(FileObject appView) { if (!ModuleUtils.isTemplate(category)) { return Collections.emptySet(); } FileObject target = appView; PhpClass phpClass = getPhpClass(); if (phpClass == null) { return Collections.emptySet(); } final Set<Pair<String, PhpClass>> phpClasses = new HashSet<>(); try { ParserManager.parse(Collections.singleton(Source.create(target)), new UserTask() { @Override public void run(ResultIterator resultIterator) throws Exception { ParserResult result = (ParserResult) resultIterator.getParserResult(); if (result == null) { return; } ViewVisitor visitor = new ViewVisitor(phpModule); visitor.scan(Utils.getRoot(result)); phpClasses.addAll(visitor.getPhpClasses()); } }); } catch (ParseException ex) { LOGGER.log(Level.WARNING, null, ex); } for (Pair<String, PhpClass> clazz : phpClasses) { phpClass.addField(clazz.first(), clazz.second(), clazz.second().getFile(), 0); } return Collections.singleton(phpClass); } @CheckForNull private PhpClass getPhpClass() { String fullyQualifiedName; String className; switch (category) { case CONTROLLER: // XXX use AppController? className = "Controller"; // NOI18N fullyQualifiedName = "\\Cake\\Controller\\Controller"; // NOI18N break; case COMPONENT: className = "Component"; // NOI18N fullyQualifiedName = "\\Cake\\Controller\\Component"; // NOI18N break; case HELPER: className = "Helper"; // NOI18N fullyQualifiedName = "\\Cake\\View\\Helper"; // NOI18N break; case TEMPLATE: // fallthrough case TEMPLATE_CELL: case ELEMENT: case EMAIL: case ERROR: case PAGES: case LAYOUT: // XXX use AppView? className = "View"; // NOI18N fullyQualifiedName = "\\Cake\\View\\View"; // NOI18N break; default: return null; } return new PhpClass(className, fullyQualifiedName); } private boolean isEnabledCategory(Category category) { return category == Category.CONTROLLER || category == Category.COMPONENT || category == Category.TEMPLATE || category == Category.TEMPLATE_CELL || category == Category.ELEMENT || category == Category.EMAIL || category == Category.ERROR || category == Category.LAYOUT || category == Category.PAGES || category == Category.HELPER; } }