blob: 7711bd5d85b19a353639cdc483c42b8206672c2f [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.verification;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.netbeans.editor.BaseDocument;
import org.netbeans.modules.csl.api.Hint;
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.parser.PHPParseResult;
import org.netbeans.modules.php.editor.parser.astnodes.ASTNode;
import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.Identifier;
import org.netbeans.modules.php.editor.parser.astnodes.InterfaceDeclaration;
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.TraitDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.TypeDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
/**
*
* @author Ondrej Brejla <obrejla@netbeans.org>
*/
public abstract class PSR0Hint extends HintRule {
@Override
public void invoke(PHPRuleContext context, List<Hint> result) {
PHPParseResult phpParseResult = (PHPParseResult) context.parserResult;
if (phpParseResult.getProgram() != null) {
FileObject fileObject = phpParseResult.getSnapshot().getSource().getFileObject();
if (fileObject != null) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
CheckVisitor checkVisitor = createVisitor(fileObject, context.doc);
phpParseResult.getProgram().accept(checkVisitor);
if (CancelSupport.getDefault().isCancelled()) {
return;
}
result.addAll(checkVisitor.getHints());
}
}
}
abstract CheckVisitor createVisitor(FileObject fileObject, BaseDocument baseDocument);
public static class NamespaceDeclarationHint extends PSR0Hint {
private static final String HINT_ID = "PSR0.Hint.Namespace"; //NOI18N
@Override
CheckVisitor createVisitor(FileObject fileObject, BaseDocument baseDocument) {
return new NamespaceVisitor(this, fileObject, baseDocument);
}
private static final class NamespaceVisitor extends CheckVisitor {
public NamespaceVisitor(PSR0Hint psr0hint, FileObject fileObject, BaseDocument baseDocument) {
super(psr0hint, fileObject, baseDocument);
}
@Override
@NbBundle.Messages("PSR0WrongNamespaceNameHintText=Namespace declaration name doesn't correspond to current directory structure.")
public void visit(NamespaceDeclaration node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
NamespaceName namespaceName = node.getName();
if (namespaceName != null) {
String currentNamespaceName = CodeUtils.extractQualifiedName(namespaceName);
String namespaceNameToPath = currentNamespaceName.replace('\\', File.separatorChar);
String fileDirPath = getFile().getParent();
if (!fileDirPath.contains(namespaceNameToPath)) {
createHint(namespaceName, Bundle.PSR0WrongNamespaceNameHintText());
}
}
super.visit(node);
}
}
@Override
public String getId() {
return HINT_ID;
}
@Override
@NbBundle.Messages("PSR0NamespaceHintDesc=")
public String getDescription() {
return Bundle.PSR0NamespaceHintDesc();
}
@Override
@NbBundle.Messages("PSR0NamespaceHintDisp=Namespace Declaration")
public String getDisplayName() {
return Bundle.PSR0NamespaceHintDisp();
}
}
public static class TypeDeclarationHint extends PSR0Hint {
private static final String HINT_ID = "PSR0.Hint.Type"; //NOI18N
@Override
CheckVisitor createVisitor(FileObject fileObject, BaseDocument baseDocument) {
return new TypeVisitor(this, fileObject, baseDocument);
}
private static final class TypeVisitor extends CheckVisitor {
private static final String PHP_FILE_EXTENSION = ".php"; //NOI18N
public TypeVisitor(PSR0Hint psr0hint, FileObject fileObject, BaseDocument baseDocument) {
super(psr0hint, fileObject, baseDocument);
}
@Override
public void visit(ClassDeclaration node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
processTypeDeclaration(node);
super.visit(node);
}
@Override
public void visit(InterfaceDeclaration node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
processTypeDeclaration(node);
super.visit(node);
}
@Override
public void visit(TraitDeclaration node) {
if (CancelSupport.getDefault().isCancelled()) {
return;
}
processTypeDeclaration(node);
super.visit(node);
}
@NbBundle.Messages("PSR0WrongTypeNameHintText=Type declaration name doesn't correspond to current file path.")
private void processTypeDeclaration(TypeDeclaration node) {
String currentTypeName = CodeUtils.extractTypeName(node);
String typeNameToPath = currentTypeName.replace('_', File.separatorChar);
String filePath = getFile().getPath();
if (!filePath.endsWith(typeNameToPath + PHP_FILE_EXTENSION)) {
Identifier name = node.getName();
createHint(name, Bundle.PSR0WrongTypeNameHintText());
}
}
}
@Override
public String getId() {
return HINT_ID;
}
@Override
@NbBundle.Messages("PSR0TypeHintDesc=")
public String getDescription() {
return Bundle.PSR0TypeHintDesc();
}
@Override
@NbBundle.Messages("PSR0TypeHintDisp=Type Declaration")
public String getDisplayName() {
return Bundle.PSR0TypeHintDisp();
}
}
private abstract static class CheckVisitor extends DefaultVisitor {
private final PSR0Hint psr0hint;
private final FileObject fileObject;
private final BaseDocument baseDocument;
private final File file;
private final List<Hint> hints;
public CheckVisitor(PSR0Hint psr0hint, FileObject fileObject, BaseDocument baseDocument) {
this.psr0hint = psr0hint;
this.fileObject = fileObject;
this.baseDocument = baseDocument;
this.file = FileUtil.toFile(fileObject);
this.hints = new ArrayList<>();
}
public List<Hint> getHints() {
return hints;
}
@NbBundle.Messages({
"# {0} - Text which describes the violation",
"PSR0ViolationHintText=PSR-0 Violation:\n{0}"
})
protected void createHint(ASTNode node, String message) {
OffsetRange offsetRange = new OffsetRange(node.getStartOffset(), node.getEndOffset());
if (psr0hint.showHint(offsetRange, baseDocument)) {
hints.add(new Hint(
psr0hint,
Bundle.PSR0ViolationHintText(message),
fileObject,
offsetRange,
null,
500));
}
}
protected File getFile() {
return file;
}
}
@Override
public boolean getDefaultEnabled() {
return false;
}
}