| /* |
| * 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.php.editor.actions; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import org.netbeans.modules.csl.api.OffsetRange; |
| import org.netbeans.modules.php.editor.api.AliasedName; |
| import org.netbeans.modules.php.editor.api.QualifiedName; |
| import org.netbeans.modules.php.editor.model.ModelUtils; |
| import org.netbeans.modules.php.editor.model.NamespaceScope; |
| import org.netbeans.modules.php.editor.model.UseScope; |
| import org.netbeans.modules.php.editor.model.impl.Type; |
| import org.netbeans.modules.php.editor.parser.PHPParseResult; |
| import org.netbeans.modules.php.editor.parser.astnodes.ASTNode; |
| import org.netbeans.modules.php.editor.parser.astnodes.NamespaceDeclaration; |
| import org.netbeans.modules.php.editor.parser.astnodes.NamespaceName; |
| import org.netbeans.modules.php.editor.parser.astnodes.PHPDocTypeNode; |
| import org.netbeans.modules.php.editor.parser.astnodes.Program; |
| import org.netbeans.modules.php.editor.parser.astnodes.UseStatement; |
| import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor; |
| |
| /** |
| * |
| * @author Ondrej Brejla <obrejla@netbeans.org> |
| */ |
| public class UsedNamesCollector { |
| private final PHPParseResult parserResult; |
| private final int caretPosition; |
| private Map<String, List<UsedNamespaceName>> possibleNames; |
| private static final List<String> SPECIAL_NAMES = new LinkedList<>(); |
| |
| static { |
| SPECIAL_NAMES.add("parent"); //NOI18N |
| SPECIAL_NAMES.add("self"); //NOI18N |
| SPECIAL_NAMES.add("static"); //NOI18N |
| } |
| |
| public UsedNamesCollector(final PHPParseResult parserResult, final int caretPosition) { |
| this.parserResult = parserResult; |
| this.caretPosition = caretPosition; |
| } |
| |
| public Map<String, List<UsedNamespaceName>> collectNames() { |
| NamespaceScope namespaceScope = ModelUtils.getNamespaceScope(parserResult.getModel().getFileScope(), caretPosition); |
| assert namespaceScope != null; |
| OffsetRange offsetRange = namespaceScope.getBlockRange(); |
| Collection<? extends UseScope> declaredUses = namespaceScope.getAllDeclaredSingleUses(); |
| NamespaceNameVisitor namespaceNameVisitor = new NamespaceNameVisitor(offsetRange); |
| parserResult.getProgram().accept(namespaceNameVisitor); |
| possibleNames = namespaceNameVisitor.getExistingNames(); |
| return filterNamesWithoutUses(declaredUses); |
| } |
| |
| private Map<String, List<UsedNamespaceName>> filterNamesWithoutUses(final Collection<? extends UseScope> declaredUses) { |
| final Map<String, List<UsedNamespaceName>> result = new HashMap<>(); |
| for (Map.Entry<String, List<UsedNamespaceName>> entry : possibleNames.entrySet()) { |
| if (!existsUseForTypeName(declaredUses, QualifiedName.create(entry.getKey()))) { |
| result.put(entry.getKey(), entry.getValue()); |
| } |
| } |
| return result; |
| } |
| |
| private boolean existsUseForTypeName(final Collection<? extends UseScope> declaredUses, final QualifiedName typeName) { |
| boolean result = false; |
| String firstSegmentName = typeName.getSegments().getFirst(); |
| for (UseScope useElement : declaredUses) { |
| AliasedName aliasName = useElement.getAliasedName(); |
| if (aliasName != null) { |
| if (firstSegmentName.equals(aliasName.getAliasName())) { |
| result = true; |
| break; |
| } |
| } else { |
| if (useElement.getName().endsWith(firstSegmentName)) { |
| result = true; |
| break; |
| } |
| } |
| } |
| return result; |
| } |
| |
| private static class NamespaceNameVisitor extends DefaultVisitor { |
| private final OffsetRange offsetRange; |
| private final Map<String, List<UsedNamespaceName>> existingNames = new HashMap<>(); |
| |
| public NamespaceNameVisitor(OffsetRange offsetRange) { |
| this.offsetRange = offsetRange; |
| } |
| |
| @Override |
| public void scan(ASTNode node) { |
| if (isNodeForScan(node)) { |
| super.scan(node); |
| } |
| } |
| |
| private boolean isNodeForScan(final ASTNode node) { |
| return node != null && isInNamespace(node) && !(node instanceof UseStatement); |
| } |
| |
| private boolean isInNamespace(ASTNode node) { |
| return offsetRange.containsInclusive(node.getStartOffset()) || offsetRange.containsInclusive(node.getEndOffset()); |
| } |
| |
| @Override |
| public void visit(Program node) { |
| scan(node.getStatements()); |
| scan(node.getComments()); |
| } |
| |
| @Override |
| public void visit(NamespaceDeclaration node) { |
| scan(node.getBody()); |
| } |
| |
| @Override |
| public void visit(NamespaceName node) { |
| UsedNamespaceName usedName = new UsedNamespaceName(node); |
| if (isValidTypeName(usedName.getName())) { |
| processUsedName(usedName); |
| } |
| } |
| |
| @Override |
| public void visit(PHPDocTypeNode node) { |
| UsedNamespaceName usedName = new UsedNamespaceName(node); |
| if (isValidTypeName(usedName.getName())) { |
| processUsedName(usedName); |
| } |
| } |
| |
| private boolean isValidTypeName(final String typeName) { |
| return !SPECIAL_NAMES.contains(typeName) && !Type.isPrimitive(typeName); |
| } |
| |
| private void processUsedName(final UsedNamespaceName usedName) { |
| List<UsedNamespaceName> usedNames = existingNames.get(usedName.getName()); |
| if (usedNames == null) { |
| usedNames = new LinkedList<>(); |
| existingNames.put(usedName.getName(), usedNames); |
| } |
| usedNames.add(usedName); |
| } |
| |
| public Map<String, List<UsedNamespaceName>> getExistingNames() { |
| return Collections.unmodifiableMap(existingNames); |
| } |
| |
| } |
| |
| } |