blob: b981b3f792add81d641bff29129b78bcdb2ba9b9 [file] [log] [blame]
/*
* 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);
}
}
}