blob: 4ae751a292a41df1a5da46f7c76ef06acb2daac8 [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.parser;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.spi.support.CancelSupport;
import org.netbeans.modules.php.editor.CodeUtils;
import org.netbeans.modules.php.editor.api.QualifiedName;
import org.netbeans.modules.php.editor.parser.astnodes.GroupUseStatementPart;
import org.netbeans.modules.php.editor.parser.astnodes.Identifier;
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.SingleUseStatementPart;
import org.netbeans.modules.php.editor.parser.astnodes.UseStatementPart;
import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor;
/**
*
* @author Ondrej Brejla <obrejla@netbeans.org>
*/
public class UnusedUsesCollector extends DefaultVisitor {
private static final String NAMESPACE_SEPARATOR = "\\"; //NOI18N
private final PHPParseResult parserResult;
private final Map<String, UnusedOffsetRanges> unusedUsesOffsetRanges;
public UnusedUsesCollector(PHPParseResult parserResult) {
assert parserResult != null;
this.parserResult = parserResult;
unusedUsesOffsetRanges = new HashMap<>();
}
public Collection<UnusedOffsetRanges> collect() {
Program program = parserResult.getProgram();
if (program != null) {
program.accept(this);
}
return unusedUsesOffsetRanges.values();
}
@Override
public void visit(Program program) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
scan(program.getStatements());
scan(program.getComments());
}
@Override
public void visit(PHPDocTypeNode node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
QualifiedName typeName = QualifiedName.create(node.getValue());
if (unusedUsesOffsetRanges.size() > 0 && !typeName.getKind().isFullyQualified()) {
String firstSegmentName = typeName.getSegments().getFirst();
processFirstSegmentName(firstSegmentName);
}
}
@Override
public void visit(NamespaceName node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
if (unusedUsesOffsetRanges.size() > 0 && !node.isGlobal()) {
Identifier firstSegment = node.getSegments().get(0);
String firstSegmentName = firstSegment.getName();
processFirstSegmentName(firstSegmentName);
}
}
private void processFirstSegmentName(final String firstSegmentName) {
Set<String> namesToRemove = new HashSet<>();
for (String name : unusedUsesOffsetRanges.keySet()) {
QualifiedName qualifiedUseName = QualifiedName.create(name);
if (qualifiedUseName.getSegments().getLast().equals(firstSegmentName)) {
namesToRemove.add(name);
}
}
for (String nameToRemove : namesToRemove) {
unusedUsesOffsetRanges.remove(nameToRemove);
}
}
@Override
public void visit(UseStatement node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
List<UseStatementPart> parts = node.getParts();
if (parts.size() == 1
&& parts.get(0) instanceof SingleUseStatementPart) {
String correctName = getCorrectName((SingleUseStatementPart) parts.get(0));
OffsetRange offsetRange = new OffsetRange(node.getStartOffset(), node.getEndOffset());
unusedUsesOffsetRanges.put(correctName, new UnusedOffsetRanges(offsetRange, offsetRange));
} else {
processUseStatementsParts(parts);
}
}
private String getCorrectName(SingleUseStatementPart useStatementPart) {
Identifier alias = useStatementPart.getAlias();
String identifierName;
if (alias != null) {
identifierName = alias.getName();
} else {
NamespaceName name = useStatementPart.getName();
identifierName = CodeUtils.extractQualifiedName(name);
if (name.isGlobal()) {
identifierName = NAMESPACE_SEPARATOR + identifierName;
}
}
return identifierName;
}
// XXX endOffset should be start offset of the next UseStatementPart
private void processUseStatementsParts(final List<UseStatementPart> parts) {
int lastStartOffset = -1;
int partsSize = parts.size();
for (int i = 0; i < partsSize; i++) {
UseStatementPart useStatementPart = parts.get(i);
int endOffset;
if (useStatementPart instanceof SingleUseStatementPart) {
SingleUseStatementPart singleUseStatementPart = (SingleUseStatementPart) useStatementPart;
if (lastStartOffset == -1) {
lastStartOffset = singleUseStatementPart.getStartOffset();
}
// XXX
// if (i == 0) {
// lastStartOffset = useStatementPart.getStartOffset();
// assert i + 1 < parts.size();
// SingleUseStatementPart nextPart = parts.get(i + 1);
// endOffset = nextPart.getStartOffset();
// }
endOffset = singleUseStatementPart.getEndOffset();
processSingleUseStatementPart(singleUseStatementPart, lastStartOffset, endOffset);
lastStartOffset = singleUseStatementPart.getEndOffset();
} else if (useStatementPart instanceof GroupUseStatementPart) {
GroupUseStatementPart groupUseStatementPart = (GroupUseStatementPart) useStatementPart;
List<SingleUseStatementPart> items = groupUseStatementPart.getItems();
if (items.isEmpty()) {
continue;
}
if (lastStartOffset == -1) {
lastStartOffset = items.get(0).getStartOffset();
}
for (SingleUseStatementPart item : items) {
endOffset = item.getEndOffset();
processSingleUseStatementPart(item, lastStartOffset, endOffset);
lastStartOffset = item.getEndOffset();
}
} else {
assert false : "Unexpected class type: " + useStatementPart.getClass().getName(); // NOI18N
}
}
}
private void processSingleUseStatementPart(SingleUseStatementPart singleUseStatementPart, int replaceStartOffset, int replaceEndOffset) {
String correctName = getCorrectName(singleUseStatementPart);
OffsetRange rangeToVisualise = new OffsetRange(singleUseStatementPart.getStartOffset(), singleUseStatementPart.getEndOffset());
OffsetRange rangeToReplace = new OffsetRange(replaceStartOffset, replaceEndOffset);
unusedUsesOffsetRanges.put(correctName, new UnusedOffsetRanges(rangeToVisualise, rangeToReplace));
}
public static final class UnusedOffsetRanges {
private final OffsetRange rangeToVisualise;
private final OffsetRange rangeToReplace;
private UnusedOffsetRanges(final OffsetRange rangeToVisualise, final OffsetRange rangeToReplace) {
this.rangeToVisualise = rangeToVisualise;
this.rangeToReplace = rangeToReplace;
}
public OffsetRange getRangeToVisualise() {
return rangeToVisualise;
}
public OffsetRange getRangeToReplace() {
return rangeToReplace;
}
}
}