| /* |
| * 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; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.swing.text.Document; |
| import org.netbeans.api.annotations.common.CheckForNull; |
| import org.netbeans.modules.php.api.PhpVersion; |
| import org.netbeans.modules.php.editor.model.UseScope; |
| import org.netbeans.modules.php.editor.model.impl.Type; |
| import org.netbeans.modules.php.editor.model.nodes.NamespaceDeclarationInfo; |
| import org.netbeans.modules.php.editor.parser.astnodes.ArrayAccess; |
| import org.netbeans.modules.php.editor.parser.astnodes.ArrayCreation; |
| import org.netbeans.modules.php.editor.parser.astnodes.Assignment; |
| import org.netbeans.modules.php.editor.parser.astnodes.CatchClause; |
| import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration; |
| import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation; |
| import org.netbeans.modules.php.editor.parser.astnodes.ClassName; |
| import org.netbeans.modules.php.editor.parser.astnodes.Expression; |
| import org.netbeans.modules.php.editor.parser.astnodes.FieldAccess; |
| import org.netbeans.modules.php.editor.parser.astnodes.FormalParameter; |
| import org.netbeans.modules.php.editor.parser.astnodes.FunctionDeclaration; |
| import org.netbeans.modules.php.editor.parser.astnodes.FunctionInvocation; |
| import org.netbeans.modules.php.editor.parser.astnodes.FunctionName; |
| 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.InfixExpression; |
| import org.netbeans.modules.php.editor.parser.astnodes.MethodDeclaration; |
| import org.netbeans.modules.php.editor.parser.astnodes.MethodInvocation; |
| import org.netbeans.modules.php.editor.parser.astnodes.NamespaceName; |
| import org.netbeans.modules.php.editor.parser.astnodes.NullableType; |
| import org.netbeans.modules.php.editor.parser.astnodes.Reference; |
| import org.netbeans.modules.php.editor.parser.astnodes.Scalar; |
| import org.netbeans.modules.php.editor.parser.astnodes.SingleUseStatementPart; |
| import org.netbeans.modules.php.editor.parser.astnodes.StaticConstantAccess; |
| import org.netbeans.modules.php.editor.parser.astnodes.StaticDispatch; |
| import org.netbeans.modules.php.editor.parser.astnodes.StaticMethodInvocation; |
| import org.netbeans.modules.php.editor.parser.astnodes.TypeDeclaration; |
| import org.netbeans.modules.php.editor.parser.astnodes.UnaryOperation; |
| import org.netbeans.modules.php.editor.parser.astnodes.UnaryOperation.Operator; |
| import org.netbeans.modules.php.editor.parser.astnodes.UseStatement; |
| import org.netbeans.modules.php.editor.parser.astnodes.Variable; |
| import org.netbeans.modules.php.editor.parser.astnodes.Variadic; |
| import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor; |
| import org.netbeans.modules.php.project.api.PhpLanguageProperties; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.DataObject; |
| import org.openide.util.Parameters; |
| |
| /** |
| * |
| * @author tomslot |
| */ |
| public final class CodeUtils { |
| public static final String FUNCTION_TYPE_PREFIX = "@fn:"; |
| public static final String METHOD_TYPE_PREFIX = "@mtd:"; |
| public static final String STATIC_METHOD_TYPE_PREFIX = "@static.mtd:"; |
| public static final String NULLABLE_TYPE_PREFIX = "?"; // NOI18N |
| private static final Logger LOGGER = Logger.getLogger(CodeUtils.class.getName()); |
| |
| private CodeUtils() { |
| } |
| |
| @CheckForNull |
| public static FileObject getFileObject(Document doc) { |
| Object sdp = doc.getProperty(Document.StreamDescriptionProperty); |
| if (sdp instanceof FileObject) { |
| return (FileObject) sdp; |
| } |
| if (sdp instanceof DataObject) { |
| return ((DataObject) sdp).getPrimaryFile(); |
| } |
| return null; |
| } |
| |
| public static UseScope.Type mapType(UseStatement.Type type) { |
| UseScope.Type newType = null; |
| switch (type) { |
| case CONST: |
| newType = UseScope.Type.CONST; |
| break; |
| case FUNCTION: |
| newType = UseScope.Type.FUNCTION; |
| break; |
| case TYPE: |
| newType = UseScope.Type.TYPE; |
| break; |
| default: |
| assert false : "Unknown type: " + type; |
| } |
| return newType; |
| } |
| |
| // XXX move to proper place |
| /** |
| * Compounds full namespace for the given part of group use. |
| * @param groupUseStatementPart group use part |
| * @param singleUseStatementPart part to be resolved |
| * @param baseOffsets if {@code true}, offsets of base namespace name are used |
| * @return full namespace for the given part of group use |
| */ |
| public static NamespaceName compoundName(GroupUseStatementPart groupUseStatementPart, SingleUseStatementPart singleUseStatementPart, boolean baseOffsets) { |
| assert groupUseStatementPart != null; |
| assert singleUseStatementPart != null; |
| assert groupUseStatementPart.getItems().contains(singleUseStatementPart) : singleUseStatementPart + " not found in: " + groupUseStatementPart.getItems(); |
| NamespaceName baseNamespaceName = groupUseStatementPart.getBaseNamespaceName(); |
| NamespaceName namespaceName = singleUseStatementPart.getName(); |
| List<Identifier> segments = new ArrayList<>(baseNamespaceName.getSegments().size() + namespaceName.getSegments().size()); |
| segments.addAll(baseNamespaceName.getSegments()); |
| segments.addAll(namespaceName.getSegments()); |
| int start; |
| int end; |
| if (baseOffsets) { |
| start = baseNamespaceName.getStartOffset(); |
| end = baseNamespaceName.getEndOffset(); |
| } else { |
| start = namespaceName.getStartOffset(); |
| end = namespaceName.getEndOffset(); |
| } |
| return new NamespaceName(start, end, segments, baseNamespaceName.isGlobal(), baseNamespaceName.isCurrent()); |
| } |
| |
| /** |
| * Checks whether the given name is synthetic name. It means that |
| * the name starts with "#". |
| * @param name name to be checked |
| * @return {@code true} if the given name is synthetic |
| */ |
| public static boolean isSyntheticTypeName(String name) { |
| assert name != null; |
| return !name.isEmpty() |
| && name.charAt(0) == '#'; // NOI18N |
| } |
| |
| /** |
| * Checks whether the given name is synthetic name. It means that |
| * the name contains ":" (e.g. LambdaFunctionDeclaration:11). |
| * @param name name to be checked |
| * @return {@code true} if the given name is synthetic |
| */ |
| public static boolean isSyntheticFunctionName(String name) { |
| assert name != null; |
| return !name.isEmpty() |
| && name.contains(":"); // NOI18N |
| } |
| |
| public static PhpVersion getPhpVersion(FileObject file) { |
| assert file != null; |
| return PhpLanguageProperties.forFileObject(file).getPhpVersion(); |
| } |
| |
| public static boolean isPhpVersion(FileObject file, PhpVersion version) { |
| assert file != null; |
| assert version != null; |
| return getPhpVersion(file) == version; |
| } |
| |
| public static boolean isPhpVersionLessThan(FileObject file, PhpVersion version) { |
| assert file != null; |
| assert version != null; |
| return getPhpVersion(file).compareTo(version) < 0; |
| } |
| |
| public static boolean isPhpVersionGreaterThan(FileObject file, PhpVersion version) { |
| assert file != null; |
| assert version != null; |
| return getPhpVersion(file).compareTo(version) > 0; |
| } |
| |
| /** |
| * @return {@code true} if the {@link StaticDispatch#getDispatcher() dispatcher} |
| * is not just identifier or a namespace name. |
| */ |
| public static boolean isUniformVariableSyntax(StaticDispatch dispatch) { |
| assert dispatch != null; |
| return isUniformVariableSyntax(dispatch.getDispatcher()); |
| } |
| |
| /** |
| * @return {@code true} if the given expression |
| * is not just identifier or a namespace name. |
| */ |
| public static boolean isUniformVariableSyntax(Expression expression) { |
| assert expression != null; |
| return extractUnqualifiedName(expression) == null; |
| } |
| |
| @CheckForNull |
| public static Identifier extractUnqualifiedIdentifier(Expression typeName) { |
| Parameters.notNull("typeName", typeName); // NOI18N |
| if (typeName instanceof Identifier) { |
| return (Identifier) typeName; |
| } else if (typeName instanceof NamespaceName) { |
| return extractUnqualifiedIdentifier((NamespaceName) typeName); |
| } else if (typeName instanceof Variable) { |
| Variable v = (Variable) typeName; |
| return extractUnqualifiedIdentifier(v.getName()); // #167863 |
| } else if (typeName instanceof FieldAccess) { |
| return extractUnqualifiedIdentifier(((FieldAccess) typeName).getField()); // #167863 |
| } else if (typeName instanceof NullableType) { |
| return extractUnqualifiedIdentifier(((NullableType) typeName).getType()); |
| } |
| //TODO: php5.3 !!! |
| //assert false : typeName.getClass(); //NOI18N |
| return null; |
| } |
| |
| /** |
| * Extract unqualified name for Identifier, NamespaceName, and NullableType. |
| * |
| * @param typeName The type name |
| * @return The type name. If it is a nullable type, the name is returned with "?" |
| */ |
| @CheckForNull |
| public static String extractUnqualifiedName(Expression typeName) { |
| Parameters.notNull("typeName", typeName); // NOI18N |
| if (typeName instanceof Identifier) { |
| return ((Identifier) typeName).getName(); |
| } else if (typeName instanceof NamespaceName) { |
| return extractUnqualifiedName((NamespaceName) typeName); |
| } else if (typeName instanceof NullableType) { |
| return NULLABLE_TYPE_PREFIX + extractUnqualifiedName(((NullableType) typeName).getType()); |
| } |
| |
| //TODO: php5.3 !!! |
| //assert false : "[php5.3] className Expression instead of Identifier"; //NOI18N |
| return null; |
| } |
| |
| /** |
| * Extract qualified name for Identifier, NamespaceName, and NullableType. |
| * |
| * @param typeName The type name |
| * @return The type name. If it is a nullable type, the name is returned with "?" |
| */ |
| @CheckForNull |
| public static String extractQualifiedName(Expression typeName) { |
| Parameters.notNull("typeName", typeName); // NOI18N |
| if (typeName instanceof Identifier) { |
| return ((Identifier) typeName).getName(); |
| } else if (typeName instanceof NamespaceName) { |
| return extractQualifiedName((NamespaceName) typeName); |
| } else if (typeName instanceof NullableType) { |
| NullableType nullableType = (NullableType) typeName; |
| return NULLABLE_TYPE_PREFIX + extractQualifiedName(nullableType.getType()); |
| } |
| assert false : typeName.getClass(); |
| return null; |
| } |
| |
| // XXX not only class name anymore in php7+ |
| public static String extractUnqualifiedClassName(StaticDispatch dispatch) { |
| Parameters.notNull("dispatch", dispatch); |
| Expression dispatcher = dispatch.getDispatcher(); |
| return extractUnqualifiedName(dispatcher); |
| } |
| |
| public static String extractUnqualifiedTypeName(FormalParameter param) { |
| Parameters.notNull("param", param); |
| Expression typeName = param.getParameterType(); |
| return typeName != null ? extractUnqualifiedName(typeName) : null; |
| } |
| |
| public static List<String> extractUnqualifiedTypeName(CatchClause catchClause) { |
| Parameters.notNull("catchClause", catchClause); |
| List<String> typeNames = new ArrayList<>(); |
| for (Expression className : catchClause.getClassNames()) { |
| if (className != null) { |
| String typeName = extractUnqualifiedName(className); |
| if (typeName != null) { |
| typeNames.add(typeName); |
| } |
| } |
| } |
| return typeNames; |
| } |
| |
| public static String extractUnqualifiedSuperClassName(ClassDeclaration clsDeclaration) { |
| Parameters.notNull("clsDeclaration", clsDeclaration); |
| Expression clsName = clsDeclaration.getSuperClass(); |
| return clsName != null ? extractUnqualifiedName(clsName) : null; |
| } |
| |
| @CheckForNull |
| public static String extractUnqualifiedSuperClassName(ClassInstanceCreation classInstanceCreation) { |
| assert classInstanceCreation != null; |
| assert classInstanceCreation.isAnonymous() : classInstanceCreation; |
| Expression clsName = classInstanceCreation.getSuperClass(); |
| return clsName != null ? extractUnqualifiedName(clsName) : null; |
| } |
| |
| public static String extractUnqualifiedName(NamespaceName namespaceName) { |
| final List<Identifier> segments = namespaceName.getSegments(); |
| return segments.get(segments.size() - 1).getName(); |
| } |
| |
| public static String extractQualifiedName(NamespaceName namespaceName) { |
| Parameters.notNull("namespaceName", namespaceName); |
| StringBuilder sb = new StringBuilder(); |
| final List<Identifier> segments = namespaceName.getSegments(); |
| if (namespaceName.isGlobal()) { |
| sb.append(NamespaceDeclarationInfo.NAMESPACE_SEPARATOR); |
| } |
| for (Iterator<Identifier> it = segments.iterator(); it.hasNext();) { |
| Identifier identifier = it.next(); |
| sb.append(identifier.getName()); |
| if (it.hasNext()) { |
| sb.append(NamespaceDeclarationInfo.NAMESPACE_SEPARATOR); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| public static Identifier extractUnqualifiedIdentifier(NamespaceName namespaceName) { |
| final List<Identifier> segments = namespaceName.getSegments(); |
| if (segments.size() >= 1) { |
| return segments.get(segments.size() - 1); |
| } |
| //TODO: php5.3 !!! |
| //assert false : "[php5.3] className Expression instead of Identifier"; //NOI18N |
| return null; |
| } |
| //TODO: rewrite for php53 |
| public static String extractClassName(ClassName clsName) { |
| assert clsName != null; |
| Expression name = clsName.getName(); |
| while (name instanceof Variable || name instanceof FieldAccess) { |
| if (name instanceof Variable) { |
| Variable var = (Variable) name; |
| name = var.getName(); |
| } else if (name instanceof FieldAccess) { |
| FieldAccess fld = (FieldAccess) name; |
| name = fld.getField().getName(); |
| } |
| } |
| if (name instanceof NamespaceName) { |
| return extractQualifiedName((NamespaceName) name); |
| } |
| return (name instanceof Identifier) ? ((Identifier) name).getName() : ""; //NOI18N |
| } |
| |
| public static String extractClassName(ClassDeclaration clsDeclaration) { |
| return clsDeclaration.getName().getName(); |
| } |
| |
| public static String extractClassName(ClassInstanceCreation classInstanceCreation) { |
| return extractClassName(classInstanceCreation.getClassName()); |
| } |
| |
| public static String extractTypeName(TypeDeclaration typeDeclaration) { |
| return typeDeclaration.getName().getName(); |
| } |
| |
| private static final class VariableNameVisitor extends DefaultVisitor { |
| |
| private String name = null; |
| private boolean isDollared = false; |
| |
| private VariableNameVisitor() { |
| } |
| |
| private static class SingletonHolder { |
| public static final VariableNameVisitor INSTANCE = new VariableNameVisitor(); |
| } |
| |
| public static VariableNameVisitor getInstance() { |
| return SingletonHolder.INSTANCE; |
| } |
| |
| public String findName(Variable var) { |
| name = null; |
| scan(var); |
| return name; |
| } |
| |
| @Override |
| public void visit(Scalar node) { |
| final String scalarName = node.getStringValue(); |
| if ((scalarName.startsWith("'") && scalarName.endsWith("'")) |
| || (scalarName.startsWith("\"") && scalarName.endsWith("\""))) { //NOI18N |
| name = scalarName.substring(1, scalarName.length() - 1); |
| } else { |
| name = scalarName; |
| } |
| } |
| |
| @Override |
| public void visit(Variable node) { |
| isDollared = node.isDollared(); |
| super.visit(node); |
| } |
| |
| @Override |
| public void visit(InfixExpression node) { |
| } |
| |
| @Override |
| public void visit(Identifier identifier) { |
| name = isDollared ? "$" + identifier.getName() : identifier.getName(); |
| } |
| |
| @Override |
| public void visit(ArrayAccess node) { |
| scan(node.getName()); |
| } |
| } |
| |
| |
| @CheckForNull // null for RelectionVariable |
| public static String extractVariableName(Variable var) { |
| String variableName = VariableNameVisitor.getInstance().findName(var); |
| if (variableName == null) { |
| LOGGER.log(Level.FINE, "Can not retrieve variable name: {0}", var); |
| } |
| return variableName; |
| } |
| |
| @CheckForNull |
| public static String extractFormalParameterName(FormalParameter param) { |
| Expression expression = param.getParameterName(); |
| if (expression instanceof Reference) { |
| expression = ((Reference) expression).getExpression(); |
| } |
| if (expression instanceof Variadic) { |
| expression = ((Variadic) expression).getExpression(); |
| } |
| if (expression instanceof Variable) { |
| Variable variable = (Variable) expression; |
| return extractVariableName(variable); |
| } |
| return null; |
| } |
| |
| public static String extractVariableType(Assignment assignment) { |
| Expression rightSideExpression = assignment.getRightHandSide(); |
| |
| if (rightSideExpression instanceof Assignment) { |
| // handle nested assignments, e.g. $l = $m = new ObjectName; |
| return extractVariableType((Assignment) assignment.getRightHandSide()); |
| } else if (rightSideExpression instanceof Reference) { |
| Reference ref = (Reference) rightSideExpression; |
| rightSideExpression = ref.getExpression(); |
| } |
| |
| if (rightSideExpression instanceof ClassInstanceCreation) { |
| ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) rightSideExpression; |
| Expression className = classInstanceCreation.getClassName().getName(); |
| return CodeUtils.extractUnqualifiedName(className); |
| } else if (rightSideExpression instanceof ArrayCreation) { |
| return Type.ARRAY; |
| } else if (rightSideExpression instanceof FunctionInvocation) { |
| FunctionInvocation functionInvocation = (FunctionInvocation) rightSideExpression; |
| String fname = extractFunctionName(functionInvocation); |
| return FUNCTION_TYPE_PREFIX + fname; |
| } else if (rightSideExpression instanceof StaticMethodInvocation) { |
| StaticMethodInvocation staticMethodInvocation = (StaticMethodInvocation) rightSideExpression; |
| String className = CodeUtils.extractUnqualifiedClassName(staticMethodInvocation); |
| String methodName = extractFunctionName(staticMethodInvocation.getMethod()); |
| |
| if (className != null && methodName != null) { |
| return STATIC_METHOD_TYPE_PREFIX + className + '.' + methodName; |
| } |
| } else if (rightSideExpression instanceof MethodInvocation) { |
| MethodInvocation methodInvocation = (MethodInvocation) rightSideExpression; |
| String varName = null; |
| |
| if (methodInvocation.getDispatcher() instanceof Variable) { |
| Variable var = (Variable) methodInvocation.getDispatcher(); |
| varName = extractVariableName(var); |
| } |
| |
| String methodName = extractFunctionName(methodInvocation.getMethod()); |
| |
| if (varName != null && methodName != null) { |
| return METHOD_TYPE_PREFIX + varName + '.' + methodName; |
| } |
| } |
| |
| return null; |
| } |
| |
| public static String extractFunctionName(FunctionInvocation functionInvocation) { |
| return extractFunctionName(functionInvocation.getFunctionName()); |
| } |
| |
| public static String extractFunctionName(FunctionDeclaration functionDeclaration) { |
| return functionDeclaration.getFunctionName().getName(); |
| } |
| |
| public static String extractMethodName(MethodDeclaration methodDeclaration) { |
| return methodDeclaration.getFunction().getFunctionName().getName(); |
| } |
| |
| @CheckForNull |
| public static String extractFunctionName(FunctionName functionName) { |
| if (functionName.getName() instanceof Identifier) { |
| Identifier id = (Identifier) functionName.getName(); |
| return id.getName(); |
| } else if (functionName.getName() instanceof NamespaceName) { |
| return extractUnqualifiedName((NamespaceName) functionName.getName()); |
| } else if (functionName.getName() instanceof Scalar) { |
| String scalarName = ((Scalar) functionName.getName()).getStringValue(); |
| if (isQuoted(scalarName)) { |
| return scalarName.substring(1, scalarName.length() - 1); |
| } |
| } |
| if (functionName.getName() instanceof Variable) { |
| Variable var = (Variable) functionName.getName(); |
| return extractVariableName(var); |
| } |
| |
| return null; |
| } |
| |
| private static boolean isQuoted(String string) { |
| return string != null |
| && string.length() > 2 |
| && ((string.startsWith("'") && string.endsWith("'")) || (string.startsWith("\"") && string.endsWith("\""))); // NOI18N |
| } |
| |
| @CheckForNull |
| public static String getParamDefaultValue(FormalParameter param) { |
| Expression expr = param.getDefaultValue(); |
| //TODO: can be improved |
| Operator operator = null; |
| if (expr instanceof UnaryOperation) { |
| UnaryOperation unaryExpr = (UnaryOperation) expr; |
| operator = unaryExpr.getOperator(); |
| expr = unaryExpr.getExpression(); |
| } |
| if (expr instanceof Scalar) { |
| Scalar scalar = (Scalar) expr; |
| String returnValue = scalar.getStringValue(); |
| return Operator.MINUS.equals(operator) ? "-" + returnValue : returnValue; // NOI18N |
| } else if (expr instanceof NamespaceName) { |
| return extractQualifiedName((NamespaceName) expr); |
| } else if (expr instanceof ArrayCreation) { |
| return "array()"; //NOI18N |
| } else if (expr instanceof StaticConstantAccess) { |
| StaticConstantAccess staticConstantAccess = (StaticConstantAccess) expr; |
| Expression dispatcher = staticConstantAccess.getDispatcher(); |
| if (dispatcher instanceof Identifier) { |
| Identifier i = (Identifier) dispatcher; |
| return i.getName() + "::" + staticConstantAccess.getConstantName().getName(); // NOI18N |
| } else if (dispatcher instanceof NamespaceName) { |
| NamespaceName namespace = (NamespaceName) dispatcher; |
| StringBuilder sb = new StringBuilder(extractQualifiedName(namespace)); |
| return sb.append("::").append(staticConstantAccess.getConstantName().getName()).toString(); // NOI18N |
| } |
| } |
| return expr == null ? null : " "; //NOI18N |
| } |
| |
| public static String getParamDisplayName(FormalParameter param) { |
| Expression paramNameExpr = param.getParameterName(); |
| StringBuilder paramName = new StringBuilder(); |
| |
| if (paramNameExpr instanceof Variable) { |
| Variable var = (Variable) paramNameExpr; |
| Identifier id = (Identifier) var.getName(); |
| |
| if (var.isDollared()) { |
| paramName.append("$"); //NOI18N |
| } |
| |
| paramName.append(id.getName()); |
| } else if (paramNameExpr instanceof Reference) { |
| paramName.append("&"); |
| Reference reference = (Reference) paramNameExpr; |
| |
| Expression expression = reference.getExpression(); |
| if (expression instanceof Variadic) { |
| Variadic variadic = (Variadic) expression; |
| paramName.append("..."); //NOI18N |
| expression = variadic.getExpression(); |
| } |
| |
| if (expression instanceof Variable) { |
| Variable var = (Variable) reference.getExpression(); |
| |
| if (var.isDollared()) { |
| paramName.append("$"); //NOI18N |
| } |
| |
| Identifier id = (Identifier) var.getName(); |
| paramName.append(id.getName()); |
| } |
| } |
| |
| return paramName.length() == 0 ? null : paramName.toString(); |
| } |
| |
| public static boolean isConstructor(MethodDeclaration node) { |
| return "__construct".equals(extractMethodName(node)); //NOI18N |
| } |
| |
| /** |
| * Finds common namespace prefixes for the given <b>sorted</b> namespaces. |
| * <p> |
| * Note: all returned prefixes start and end with '\\'. |
| * @param namespaces input namespaces (<b>must be sorted!</b>) |
| * @return list of common namespace prefixes or empty list, never {@code null} |
| */ |
| public static List<String> getCommonNamespacePrefixes(List<String> namespaces) { |
| if (namespaces.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| LinkedList<String> fqNamespaces = new LinkedList<>(); |
| for (String namespace : namespaces) { |
| fqNamespaces.add(fullyQualifyNamespace(namespace)); |
| } |
| List<String> prefixes = new ArrayList<>(fqNamespaces.size()); |
| while (!fqNamespaces.isEmpty()) { |
| String namespace = fqNamespaces.poll(); |
| if (fqNamespaces.isEmpty()) { |
| break; |
| } |
| assert namespace.charAt(0) == '\\' : namespace; |
| int separatorIndex = namespace.indexOf('\\', 1); // NOI18N |
| if (separatorIndex == -1) { |
| // no ns separator |
| continue; |
| } |
| String prefix = namespace.substring(0, separatorIndex + 1); |
| List<String> prefixedNamespaces = new ArrayList<>(fqNamespaces.size()); |
| // get all namespaces that start with this prefix |
| for (Iterator<String> iterator = fqNamespaces.iterator(); iterator.hasNext();) { |
| String ns = iterator.next(); |
| if (ns.startsWith(prefix)) { |
| prefixedNamespaces.add(ns); |
| iterator.remove(); |
| } else { |
| break; |
| } |
| } |
| if (prefixedNamespaces.isEmpty()) { |
| // not common ns prefix |
| continue; |
| } |
| prefixedNamespaces.add(0, namespace); |
| if (prefixedNamespaces.size() > 1) { |
| // find common longest prefix |
| prefix = null; |
| for (int i = 1; i < prefixedNamespaces.size(); i++) { |
| String prev = prefixedNamespaces.get(i - 1); |
| String next = prefixedNamespaces.get(i); |
| String tmpPrefix = getCommonNamespacePrefix(prev, next); |
| assert tmpPrefix != null : prev + " :: " + next; |
| if (prefix == null) { |
| prefix = tmpPrefix; |
| } else if (prefix.length() > tmpPrefix.length()) { |
| prefix = tmpPrefix; |
| } |
| } |
| assert prefix != null : prefixedNamespaces; |
| } |
| prefixes.add(prefix); |
| } |
| return prefixes; |
| } |
| |
| @CheckForNull |
| static String getCommonNamespacePrefix(String ns1, String ns2) { |
| assert ns1 != null; |
| assert ns2 != null; |
| String fqns1 = fullyQualifyNamespace(ns1); |
| String fqns2 = fullyQualifyNamespace(ns2); |
| int index; |
| for (index = 0; index < fqns1.length() && index < fqns2.length(); index++) { |
| if (fqns1.charAt(index) != fqns2.charAt(index)) { |
| break; |
| } |
| } |
| // check shortest common prefix (e.g. '\A\') |
| if (index < 3) { |
| return null; |
| } |
| String prefix = fqns1.substring(0, index); |
| if (prefix.charAt(index - 1) == '\\') { // NOI18N |
| return prefix; |
| } |
| // find last '\' (avoid first '\') |
| int lastNsIndex = prefix.lastIndexOf('\\'); // NOI18N |
| if (lastNsIndex <= 0) { |
| // not found or the first '\' |
| return null; |
| } |
| return prefix.substring(0, lastNsIndex + 1); |
| } |
| |
| public static String fullyQualifyNamespace(String namespace) { |
| assert namespace != null; |
| if (!namespace.isEmpty() |
| && namespace.charAt(0) != '\\') { // NOI18N |
| return '\\' + namespace; // NOI18N |
| } |
| return namespace; |
| } |
| |
| /** |
| * Check whether a type name starts with "?". |
| * |
| * @param typeName a type name |
| * @return {@code true} if the name starts with "?", otherwise |
| * {@code false} |
| */ |
| public static boolean isNullableType(String typeName) { |
| if (typeName == null || typeName.isEmpty()) { |
| return false; |
| } |
| return typeName.startsWith(NULLABLE_TYPE_PREFIX); |
| } |
| |
| /** |
| * Remove the nullable type prefix("?") from the type name. |
| * |
| * @param typeName the type name |
| * @return the type name from which the prefix is removed if it is a |
| * nullable type, otherwise itself |
| */ |
| public static String removeNullableTypePrefix(String typeName) { |
| if (isNullableType(typeName)) { |
| return typeName.substring(1); |
| } |
| return typeName; |
| } |
| |
| } |