blob: e05b4e1c3518616d9df01436562e1ca39a36006d [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.elements;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.parsing.spi.indexing.support.IndexResult;
import org.netbeans.modules.php.api.editor.PhpType;
import org.netbeans.modules.php.api.editor.PhpVariable;
import org.netbeans.modules.php.editor.CodeUtils;
import org.netbeans.modules.php.editor.api.ElementQuery;
import org.netbeans.modules.php.editor.api.FileElementQuery;
import org.netbeans.modules.php.editor.api.NameKind;
import org.netbeans.modules.php.editor.api.PhpElementKind;
import org.netbeans.modules.php.editor.api.elements.TypeResolver;
import org.netbeans.modules.php.editor.api.elements.VariableElement;
import org.netbeans.modules.php.editor.index.PHPIndexer;
import org.netbeans.modules.php.editor.index.Signature;
import org.netbeans.modules.php.editor.model.impl.VariousUtils;
import org.netbeans.modules.php.editor.model.nodes.ASTNodeInfo;
import org.netbeans.modules.php.editor.parser.astnodes.ArrayAccess;
import org.netbeans.modules.php.editor.parser.astnodes.Expression;
import org.netbeans.modules.php.editor.parser.astnodes.Variable;
import org.openide.filesystems.FileObject;
import org.openide.util.Parameters;
/**
* @author Radek Matous
*/
public class VariableElementImpl extends PhpElementImpl implements VariableElement {
public static final String DOLLAR_PREFIX = "$"; //NOI18N
public static final String REFERENCE_PREFIX = "&"; //NOI18N
public static final String VARIADIC_PREFIX = "..."; //NOI18N
public static final String IDX_FIELD = PHPIndexer.FIELD_VAR;
private final Set<TypeResolver> instanceTypes;
private Set<TypeResolver> instanceFQTypes;
protected VariableElementImpl(
final String variableName,
final int offset,
final String fileUrl,
final ElementQuery elementQuery,
final Set<TypeResolver> instanceTypes,
final Set<TypeResolver> instanceFQTypes,
final boolean isDeprecated) {
super(VariableElementImpl.getName(variableName, true), null, fileUrl, offset, elementQuery, isDeprecated);
this.instanceTypes = instanceTypes;
this.instanceFQTypes = instanceFQTypes;
}
public static VariableElementImpl create(
final String variableName,
final int offset,
final String fileUrl,
final ElementQuery elementQuery,
final Set<TypeResolver> instanceTypes,
final boolean isDeprecated) {
return new VariableElementImpl(variableName, offset, fileUrl, elementQuery, instanceTypes, instanceTypes, isDeprecated);
}
public static VariableElementImpl create(
final String variableName,
final int offset,
final FileObject fo,
final ElementQuery elementQuery,
final Set<TypeResolver> instanceTypes,
final boolean isDeprecated) {
return new VariableElementImpl(variableName, offset, null, elementQuery, instanceTypes, instanceTypes, isDeprecated) {
@Override
public synchronized FileObject getFileObject() {
return fo;
}
};
}
public static Set<VariableElement> fromSignature(final IndexQueryImpl indexQuery, final IndexResult indexResult) {
return fromSignature(NameKind.empty(), indexQuery, indexResult);
}
public static Set<VariableElement> fromSignature(final NameKind query,
final IndexQueryImpl indexQuery, final IndexResult indexResult) {
final String[] values = indexResult.getValues(IDX_FIELD);
final Set<VariableElement> retval = values.length > 0
? new HashSet<VariableElement>() : Collections.<VariableElement>emptySet();
for (final String val : values) {
final VariableElement var = fromSignature(query, indexQuery, Signature.get(val));
if (var != null) {
retval.add(var);
}
}
return retval;
}
public static VariableElement fromSignature(final NameKind query,
final IndexQueryImpl indexScopeQuery, final Signature sig) {
final VariableSignatureParser signParser = new VariableSignatureParser(sig);
VariableElement retval = null;
if (matchesQuery(query, signParser)) {
retval = new VariableElementImpl(signParser.getVariableName(),
signParser.getOffset(), signParser.getFileUrl(),
indexScopeQuery, signParser.getTypes(), signParser.getFQTypes(),
signParser.isDeprecated());
}
return retval;
}
public static VariableElement fromNode(final Variable node, Set<TypeResolver> typeResolvers, final FileElementQuery fileQuery) {
Parameters.notNull("node", node);
Parameters.notNull("fileQuery", fileQuery);
ASTNodeInfo<Variable> info = ASTNodeInfo.create(node);
return new VariableElementImpl(info.getName(), info.getRange().getStart(),
fileQuery.getURL().toExternalForm(), fileQuery, typeResolvers, typeResolvers,
VariousUtils.isDeprecatedFromPHPDoc(fileQuery.getResult().getProgram(), node));
}
public static VariableElement fromFrameworks(final PhpVariable variable, final ElementQuery elementQuery) {
Parameters.notNull("variable", variable);
// XXX check nullable type?
PhpType variableType = variable.getType();
Set<TypeResolver> typeResolvers = variableType == null
? Collections.<TypeResolver>emptySet()
: Collections.<TypeResolver>singleton(new TypeResolverImpl(variableType.getFullyQualifiedName(), false));
VariableElementImpl retval = new VariableElementImpl(variable.getName(), variable.getOffset(), null, elementQuery,
typeResolvers, typeResolvers, false);
retval.setFileObject(variable.getFile());
return retval;
}
private static boolean matchesQuery(final NameKind query, VariableSignatureParser signParser) {
Parameters.notNull("query", query); //NOI18N
return (query instanceof NameKind.Empty)
|| query.matchesName(VariableElement.KIND, signParser.getVariableName());
}
@Override
public final String getName(final boolean dollared) {
final String name = getName();
final boolean startsWithDollar = name.startsWith(DOLLAR_PREFIX);
if (startsWithDollar == dollared) {
return name;
}
return dollared ? String.format("%s%s", DOLLAR_PREFIX, name) : name.substring(1); //NOI18N
}
@Override
public String getSignature() {
StringBuilder sb = new StringBuilder();
final String varName = getName();
sb.append(varName.toLowerCase()).append(Separator.SEMICOLON); //NOI18N
sb.append(varName).append(Separator.SEMICOLON); //NOI18N
sb.append(Separator.SEMICOLON); //NOI18N
sb.append(getOffset()).append(Separator.SEMICOLON); //NOI18N
sb.append(isDeprecated() ? 1 : 0).append(Separator.SEMICOLON);
sb.append(getFilenameUrl()).append(Separator.SEMICOLON);
checkSignature(sb);
return sb.toString();
}
@Override
public final PhpElementKind getPhpElementKind() {
return VariableElement.KIND;
}
@Override
public final Set<TypeResolver> getInstanceTypes() {
return instanceTypes;
}
@Override
public final Set<TypeResolver> getInstanceFQTypes() {
return instanceFQTypes;
}
private void checkSignature(StringBuilder sb) {
boolean checkEnabled = false;
assert checkEnabled = true;
if (checkEnabled) {
String retval = sb.toString();
VariableSignatureParser parser = new VariableSignatureParser(Signature.get(retval));
assert getName().equals(parser.getVariableName());
assert getOffset() == parser.getOffset();
assert getInstanceTypes().size() == parser.getTypes().size();
assert getInstanceFQTypes().size() == parser.getFQTypes().size();
}
}
private static String getName(final String name, final boolean dollared) {
final boolean startsWithDollar = name.startsWith(VariableElementImpl.DOLLAR_PREFIX);
if (startsWithDollar == dollared) {
return name;
}
return dollared ? String.format("%s%s", VariableElementImpl.DOLLAR_PREFIX, name) : name.substring(1); //NOI18N
}
private static class VariableSignatureParser {
private final Signature signature;
VariableSignatureParser(Signature signature) {
this.signature = signature;
}
String getVariableName() {
return signature.string(1);
}
int getOffset() {
return signature.integer(3);
}
Set<TypeResolver> getTypes() {
return TypeResolverImpl.parseTypes(signature.string(2));
}
Set<TypeResolver> getFQTypes() {
return TypeResolverImpl.parseTypes(signature.string(4));
}
boolean isDeprecated() {
return signature.integer(4) == 1;
}
String getFileUrl() {
return signature.string(5);
}
}
private static String toName(final Variable node) {
return CodeUtils.extractVariableName(node);
}
private static OffsetRange toOffsetRange(final Variable node) {
Expression name = node.getName();
//TODO: dangerous never ending loop
while ((name instanceof Variable)) {
while (name instanceof ArrayAccess) {
ArrayAccess access = (ArrayAccess) name;
name = access.getName();
}
if (name instanceof Variable) {
Variable var = (Variable) name;
name = var.getName();
}
}
return new OffsetRange(name.getStartOffset(), name.getEndOffset());
}
}