/******************************************************************************* * Copyright (c) 2009, 2015, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.ui; import java.util.*; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.dltk.core.*; import org.eclipse.dltk.evaluation.types.AmbiguousType; import org.eclipse.dltk.evaluation.types.MultiTypeType; import org.eclipse.dltk.evaluation.types.UnknownType; import org.eclipse.dltk.ti.types.IEvaluatedType; import org.eclipse.dltk.ui.ScriptElementLabels; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.core.ast.visitor.AbstractVisitor; import org.eclipse.php.core.project.ProjectOptions; import org.eclipse.php.internal.core.Constants; import org.eclipse.php.internal.core.typeinference.PHPSimpleTypes; import org.eclipse.php.internal.core.typeinference.evaluators.PHPEvaluationUtils; import org.eclipse.php.internal.ui.corext.codemanipulation.StubUtility; import org.eclipse.php.internal.ui.corext.template.php.CodeTemplateContextType; /** * Class that offers access to the templates contained in the 'code templates' * preference page. * * <p> * This class is not intended to be subclassed or instantiated by clients. * </p> * * @since 2.2 * * @noinstantiate This class is not intended to be instantiated by clients. * @noextend This class is not intended to be subclassed by clients. */ public class CodeGeneration { private static final String UNKNOWN_TYPE = UnknownType.INSTANCE.getTypeName(); /** * Constant ID for the type kind to be used in * {@link #getTypeBody(String, IScriptProject, String, String)} to get the * code template used for a new class type body. * * @since 3.2 */ public static final String CLASS_BODY_TEMPLATE_ID = CodeTemplateContextType.CLASSBODY_ID; /** * Constant ID for the type kind to be used in * {@link #getTypeBody(String, IScriptProject, String, String)} to get the * code template used for a new interface type body. * * @since 3.2 */ public static final String INTERFACE_BODY_TEMPLATE_ID = CodeTemplateContextType.INTERFACEBODY_ID; /** * Constant ID for the type kind to be used in * {@link #getTypeBody(String, IScriptProject, String, String)} to get the * code template used for a new enum type body. * * @since 3.2 */ public static final String ENUM_BODY_TEMPLATE_ID = CodeTemplateContextType.ENUMBODY_ID; /** * Constant ID for the type kind to be used in * {@link #getTypeBody(String, IScriptProject, String, String)} to get the * code template used for a new annotation type body. * * @since 3.2 */ public static final String ANNOTATION_BODY_TEMPLATE_ID = CodeTemplateContextType.ANNOTATIONBODY_ID; private static final String[] EMPTY = new String[0]; private CodeGeneration() { } /** * Returns the content for a new compilation unit using the 'new Java file' * code template. * * @param sp * The compilation unit to create the source for. The compilation * unit does not need to exist. * @param typeComment * The comment for the type to be created. Used when the code * template contains a <i>${typecomment}</i> variable. Can be * <code>null</code> if no comment should be added. * @param typeContent * The code of the type, including type declaration and body. * @param lineDelimiter * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the template is * undefined or empty. * @throws CoreException * Thrown when the evaluation of the code template fails. */ public static String getCompilationUnitContent(IScriptProject sp, String typeComment, String typeContent, String lineDelimiter) throws CoreException { return getCompilationUnitContent(sp, getFileComment(sp, lineDelimiter), typeComment, typeContent, lineDelimiter); } /** * Returns the content for a new compilation unit using the 'new Java file' * code template. * * @param sp * The compilation unit to create the source for. The compilation * unit does not need to exist. * @param fileComment * The file comment to be used when the code template contains a * <i>${filecomment}</i> variable. Can be <code>null</code> if no * comment should be added. * @param typeComment * The comment for the type to be created. Used when the code * template contains a <i>${typecomment}</i> variable. Can be * <code>null</code> if no comment should be added. * @param typeContent * The code of the type, including type declaration and body. * @param lineDelimiter * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the template is * undefined or empty. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.1 */ public static String getCompilationUnitContent(IScriptProject sp, String fileComment, String typeComment, String typeContent, String lineDelimiter) throws CoreException { return StubUtility.getCompilationUnitContent(sp, fileComment, typeComment, typeContent, lineDelimiter); } /** * Returns the content for a new file comment using the 'file comment' code * template. The returned content is unformatted and is not indented. * * @param sp * The compilation unit to add the comment to. The compilation * unit does not need to exist. * @param lineDelimiter * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the code template * is undefined or empty. The returned content is unformatted and is * not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.1 */ public static String getFileComment(ISourceModule sm, String lineDelimiter) throws CoreException { return StubUtility.getFileComment(sm, lineDelimiter); } public static String getFileComment(IScriptProject sp, String lineDelimiter) throws CoreException { return StubUtility.getFileComment(sp, lineDelimiter); } /** * Returns the content for a new type comment using the 'type comment' code * template. The returned content is unformatted and is not indented. * * @param sp * The compilation unit where the type is contained. The * compilation unit does not need to exist. * @param typeQualifiedName * The name of the type to which the comment is added. For inner * types the name must be qualified and include the outer types * names (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param lineDelimiter * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the code template * is undefined or empty. The returned content is unformatted and is * not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. */ public static String getTypeComment(IScriptProject sp, String typeQualifiedName, String lineDelimiter) throws CoreException { return StubUtility.getTypeComment(sp, typeQualifiedName, EMPTY, lineDelimiter); } /** * Returns the content for a new type comment using the 'type comment' code * template. The returned content is unformatted and is not indented. * * @param sp * The compilation unit where the type is contained. The * compilation unit does not need to exist. * @param typeQualifiedName * The name of the type to which the comment is added. For inner * types the name must be qualified and include the outer types * names (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param typeParameterNames * The type parameter names * @param lineDelimiter * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the code template * is undefined or empty. The returned content is unformatted and is * not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.1 */ public static String getTypeComment(IScriptProject sp, String typeQualifiedName, String[] typeParameterNames, String lineDelimiter) throws CoreException { return StubUtility.getTypeComment(sp, typeQualifiedName, typeParameterNames, lineDelimiter); } /** * Returns the content of a new new type body using the 'type body' code * templates. The returned content is unformatted and is not indented. * * @param typeKind * The type kind ID of the body template. Valid values are * {@link #CLASS_BODY_TEMPLATE_ID}, * {@link #INTERFACE_BODY_TEMPLATE_ID}, * {@link #ENUM_BODY_TEMPLATE_ID} and * {@link #ANNOTATION_BODY_TEMPLATE_ID}. * @param sp * The compilation unit where the type is contained. The * compilation unit does not need to exist. * @param typeName * The name of the type(for embedding in the template as a user * variable). * @param lineDelim * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the code template * is undefined or empty. The returned content is unformatted and is * not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.2 */ public static String getTypeBody(String typeKind, IScriptProject sp, String typeName, String lineDelim) throws CoreException { return StubUtility.getTypeBody(typeKind, sp, typeName, lineDelim); } /** * Returns the content for a new field comment using the 'field comment' * code template. The returned content is unformatted and is not indented. * * @param sp * The compilation unit where the field is contained. The * compilation unit does not need to exist. * @param fieldType * The name of the field declared type. * @param fieldName * The name of the field to which the comment is added. * @param lineDelimiter * The line delimiter to be used. * @return Returns the new content or <code>null</code> if the code template * is undefined or empty. The returned content is unformatted and is * not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.0 */ public static String getFieldComment(IScriptProject sp, IField field, String lineDelimiter) throws CoreException { Boolean isVar = false; Program program = null; // XXX: do not call SharedASTProvider.getAST() due bug 466694 and // until bug 438661 will be fixed // try { // program = SharedASTProvider.getAST(field.getSourceModule(), // SharedASTProvider.WAIT_YES, // new NullProgressMonitor()); // } catch (IOException e1) { // } if (program == null) { program = generateProgram(field, null); if (program == null) { return null; } } ASTNode elementAt = program.getElementAt(field.getSourceRange().getOffset()); // we need at least one entry (even if content is null) Expression[] expressions = new Expression[1]; ITypeBinding[] varTypes = new ITypeBinding[1]; String[] fieldNames = new String[] { field.getElementName() }; if (elementAt instanceof Variable) { isVar = true; Variable varDeclaration = (Variable) elementAt; if (varDeclaration.getParent() instanceof Assignment) { expressions[0] = ((Assignment) varDeclaration.getParent()).getRightHandSide(); varTypes[0] = expressions[0].resolveTypeBinding(); } else { varTypes[0] = varDeclaration.resolveTypeBinding(); } } else if (elementAt instanceof FieldsDeclaration) { FieldsDeclaration fieldDeclaration = (FieldsDeclaration) elementAt; Variable[] varDeclarations = fieldDeclaration.getVariableNames(); if (varDeclarations.length > 0) { expressions = fieldDeclaration.getInitialValues(); assert varDeclarations.length == expressions.length; varTypes = new ITypeBinding[varDeclarations.length]; fieldNames = new String[varDeclarations.length]; for (int i = 0; i < varDeclarations.length; i++) { varTypes[i] = varDeclarations[i].resolveTypeBinding(); fieldNames[i] = getVarName(varDeclarations[i]); } } } else if (elementAt instanceof ConstantDeclaration) { ConstantDeclaration constDeclaration = (ConstantDeclaration) elementAt; Identifier[] varDeclarations = constDeclaration.names().toArray(new Identifier[0]); if (varDeclarations.length > 0) { expressions = constDeclaration.initializers().toArray(new Expression[0]); assert varDeclarations.length == expressions.length; varTypes = new ITypeBinding[varDeclarations.length]; fieldNames = new String[varDeclarations.length]; for (int i = 0; i < varDeclarations.length; i++) { varTypes[i] = varDeclarations[i].resolveTypeBinding(); fieldNames[i] = getVarName(varDeclarations[i]); } } } String[] fieldTypes = getFieldTypes(varTypes, expressions); if (isVar || fieldTypes.length > 1) { return StubUtility.getMultipleFieldsComment(sp, fieldTypes, fieldNames, lineDelimiter); } return StubUtility.getFieldComment(sp, fieldTypes[0], fieldNames[0], lineDelimiter); } private static String getVarName(Identifier varDeclaration) { return '$' + varDeclaration.getName(); } private static String getVarName(Variable varDeclaration) { if (varDeclaration.getName() instanceof Identifier) { Identifier id = (Identifier) varDeclaration.getName(); return getVarName(id); } return ""; //$NON-NLS-1$ } /** * * @param varTypes * (can have null entries) * @param expressions * (can have null entries) * @return non-null field types */ public static String[] getFieldTypes(ITypeBinding[] varTypes, Expression[] expressions) { String[] fieldTypes = new String[expressions.length]; for (int i = 0; i < expressions.length; i++) { Expression expression = expressions[i]; ITypeBinding varType = varTypes[i]; if (expression instanceof ArrayCreation) { fieldTypes[i] = PHPSimpleTypes.ARRAY.getTypeName(); } else if (expression instanceof Scalar) { Scalar scalar = (Scalar) expression; switch (scalar.getScalarType()) { case Scalar.TYPE_INT: fieldTypes[i] = "integer"; //$NON-NLS-1$ break; case Scalar.TYPE_STRING: if (!expression.isNullExpression()) { fieldTypes[i] = PHPSimpleTypes.STRING.getTypeName(); } else { // we don't want to use varType to describe // null values (because varType.isAmbiguous() will // return true and varType.getName() will return // "NULL"), but preferably UNKNOWN_TYPE when // fieldType is null varType = null; } break; } } if (null == fieldTypes[i] && null != varType) { if (varType.isArray()) { fieldTypes[i] = PHPSimpleTypes.ARRAY.getTypeName(); } else if (varType.isAmbiguous()) { fieldTypes[i] = "Ambiguous"; //$NON-NLS-1$ } else { fieldTypes[i] = varType.getName(); } } if (null == fieldTypes[i]) { fieldTypes[i] = UNKNOWN_TYPE; } } return fieldTypes; } /** * Returns the comment for a method or constructor using the comment code * templates (constructor / method / overriding method). <code>null</code> * is returned if the template is empty. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param decl * The MethodDeclaration AST node that will be added as new * method. The node does not need to exist in an AST (no parent * needed) and does not need to resolve. See * {@link org.eclipse.jdt.core.dom.AST#newMethodDeclaration()} * for how to create such a node. * @param overridden * The binding of the method to which to add an "@see" link or * <code>null</code> if no link should be created. * @param lineDelimiter * The line delimiter to be used. * @return Returns the generated method comment or <code>null</code> if the * code template is empty. The returned content is unformatted and * not indented (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. */ /* * public static String getMethodComment(IScriptProject sp, String * declaringTypeName, MethodDeclaration decl, IMethodBinding overridden, * String lineDelimiter) throws CoreException { if (overridden != null) { * overridden= overridden.getMethodDeclaration(); String * declaringClassQualifiedName= * overridden.getDeclaringClass().getQualifiedName(); String * linkToMethodName= overridden.getName(); String[] * parameterTypesQualifiedNames= * StubUtility.getParameterTypeNamesForSeeTag(overridden); return * StubUtility.getMethodComment(sp, declaringTypeName, decl, * overridden.isDeprecated(), linkToMethodName, declaringClassQualifiedName, * parameterTypesQualifiedNames, false, lineDelimiter); } else { return * StubUtility.getMethodComment(sp, declaringTypeName, decl, false, null, * null, null, false, lineDelimiter); } } */ /** * Returns the comment for a method or constructor using the comment code * templates (constructor / method / overriding method). <code>null</code> * is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * <p> * Exception types and return type are in signature notation. e.g. a source * method declared as <code>public void foo(String text, int length)</code> * would return the array <code>{"QString;","I"}</code> as parameter types. * See {@link org.eclipse.jdt.core.Signature}. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * Name of the method. * @param paramNames * Names of the parameters for the method. * @param excTypeSig * Thrown exceptions (Signature notation). * @param retTypeSig * Return type (Signature notation) or <code>null</code> for * constructors. * @param overridden * The method that will be overridden by the created method or * <code>null</code> for non-overriding methods. If not * <code>null</code>, the method must exist. * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed comment or <code>null</code> if the * comment code template is empty. The returned content is * unformatted and not indented (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. */ public static String getMethodComment(IScriptProject sp, String declaringTypeName, String methodName, String[] paramNames, String[] excTypeSig, String retTypeSig, IMethod overridden, String lineDelimiter, List<String> exceptions) throws CoreException { return StubUtility.getMethodComment(sp, declaringTypeName, methodName, paramNames, retTypeSig, EMPTY, overridden, false, lineDelimiter, exceptions); } /** * Returns the comment for a method or constructor using the comment code * templates (constructor / method / overriding method). <code>null</code> * is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * <p> * Exception types and return type are in signature notation. e.g. a source * method declared as <code>public void foo(String text, int length)</code> * would return the array <code>{"QString;","I"}</code> as parameter types. * See {@link org.eclipse.jdt.core.Signature}. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * Name of the method. * @param paramNames * Names of the parameters for the method. * @param excTypeSig * Thrown exceptions (Signature notation). * @param retTypeSig * Return type (Signature notation) or <code>null</code> for * constructors. * @param typeParameterNames * Names of the type parameters for the method. * @param overridden * The method that will be overridden by the created method or * <code>null</code> for non-overriding methods. If not * <code>null</code>, the method must exist. * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed comment or <code>null</code> if the * comment code template is empty. The returned content is * unformatted and not indented (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.1 */ public static String getMethodComment(IScriptProject sp, String declaringTypeName, String methodName, String[] paramNames, String[] excTypeSig, String retTypeSig, String[] typeParameterNames, IMethod overridden, String lineDelimiter, List<String> exceptions) throws CoreException { return StubUtility.getMethodComment(sp, declaringTypeName, methodName, paramNames, retTypeSig, typeParameterNames, overridden, false, lineDelimiter, exceptions); } /** * Returns the comment for a method or constructor using the comment code * templates (constructor / method / overriding method). <code>null</code> * is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * * @param method * The method to be documented. The method must exist. * @param overridden * The method that will be overridden by the created method or * <code>null</code> for non-overriding methods. If not * <code>null</code>, the method must exist. * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed comment or <code>null</code> if the * comment code template is empty. The returned string is * unformatted and and has no indent (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. * Contributed by zhaozw - bug #255204 [regression] Parameters * type is not displayed in Generated element comments doc block */ public static String getMethodComment(IMethod method, IMethod overridden, String lineDelimiter) throws CoreException { // FIXME - 'retType' should be initialized to null after the // 'getReturnType will be functional, so void/c'tor will not have // 'return' tag String retType = null; String[] typeParameterNames = null; String[] parameterTypes = null; Program program = null; // XXX: do not call SharedASTProvider.getAST() due bug 466694 and // until bug 438661 will be fixed. Even without those bugs, it's still // better to force new AST generation using generateProgram(method, // null), because the cached AST (retrieved using ASTProvider and // WAIT_YES) could still be outdated when there was previously an // ASTError above the method we're handling here! // try { // program = SharedASTProvider.getAST(method.getSourceModule(), // SharedASTProvider.WAIT_YES, new NullProgressMonitor()); // } catch (IOException e1) { // } if (program == null) { program = generateProgram(method, null); if (program == null) { return null; } } // XXX: bindings can also be out-of-sync with current AST when DLTK's // document indexing is still running. It's a rare case here but ideally // we should always wait until end of indexing (sadly it could // momentarily freeze the UI): // ModelManager.getModelManager().getIndexManager().waitUntilReady(); ASTNode elementAt = null; try { elementAt = program.getElementAt(method.getSourceRange().getOffset()); } catch (IllegalArgumentException e) { program = generateProgram(method, null); if (program == null) { return null; } elementAt = program.getElementAt(method.getSourceRange().getOffset()); if (elementAt == null) { return null; } } if (!(elementAt instanceof MethodDeclaration || elementAt instanceof FunctionDeclaration || elementAt.getParent() instanceof MethodDeclaration)) { program = generateProgram(method, program); if (program == null) { return null; } elementAt = program.getElementAt(method.getSourceRange().getOffset()); } if (elementAt.getParent() instanceof MethodDeclaration) { elementAt = elementAt.getParent(); } ITypeBinding[] returnTypes = null; ITypeBinding[] typeParametersTypes = null; IFunctionBinding resolvedBinding = null; List<FormalParameter> formalParameters = null; if (elementAt instanceof MethodDeclaration) { MethodDeclaration methodDeclaration = (MethodDeclaration) elementAt; resolvedBinding = methodDeclaration.resolveMethodBinding(); formalParameters = methodDeclaration.getFunction().formalParameters(); Identifier returnType = methodDeclaration.getFunction().getReturnType(); if (returnType != null) { if (returnType.isNullable()) { StringBuilder returnTypeBuffer = new StringBuilder(); returnTypeBuffer.append(returnType.getName()).append(Constants.TYPE_SEPARATOR_CHAR) .append(PHPSimpleTypes.NULL.getTypeName()); retType = returnTypeBuffer.toString(); } else { retType = returnType.getName(); } } } else if (elementAt instanceof FunctionDeclaration) { FunctionDeclaration functionDeclaration = (FunctionDeclaration) elementAt; resolvedBinding = functionDeclaration.resolveFunctionBinding(); formalParameters = functionDeclaration.formalParameters(); Identifier returnType = functionDeclaration.getReturnType(); if (returnType != null) { if (returnType.isNullable()) { StringBuilder returnTypeBuffer = new StringBuilder(); returnTypeBuffer.append(returnType.getName()).append(Constants.TYPE_SEPARATOR_CHAR) .append(PHPSimpleTypes.NULL.getTypeName()); retType = returnTypeBuffer.toString(); } else { retType = returnType.getName(); } } } final List<String> exceptions = new ArrayList<>(); elementAt.accept(new AbstractVisitor() { @Override public boolean visit(ThrowStatement throwStatement) { Expression expression = throwStatement.getExpression(); if (expression instanceof ClassInstanceCreation) { ClassInstanceCreation cic = (ClassInstanceCreation) throwStatement.getExpression(); if (cic.getClassName().getName() instanceof Identifier) { Identifier name = (Identifier) cic.getClassName().getName(); exceptions.add(name.getName()); } } if (expression instanceof Variable) { ITypeBinding type = ((Variable) expression).resolveTypeBinding(); if (type != null) { exceptions.add(type.getName()); } } return true; } }); final List<String> newExceptions = new ArrayList<>(); final Set<String> exceptionSet = new HashSet<>(); for (Iterator<String> iterator = exceptions.iterator(); iterator.hasNext();) { String exception = iterator.next(); if (!exceptionSet.contains(exception)) { exceptionSet.add(exception); newExceptions.add(exception); } } if (formalParameters != null) { // get parameter type parameterTypes = new String[formalParameters.size()]; int i = 0; for (ASTNode node : formalParameters) { FormalParameter formalParameter = (FormalParameter) node; Expression parameterType = formalParameter.getParameterType(); if (parameterType != null) { String typeName = ((Identifier) parameterType).getName(); parameterTypes[i++] = typeName; continue; } if (formalParameter.getDefaultValue() != null && formalParameter.getDefaultValue() instanceof Scalar && !formalParameter.getDefaultValue().isNullExpression()) { Scalar scalar = (Scalar) formalParameter.getDefaultValue(); IEvaluatedType simpleType = PHPSimpleTypes.fromString(Scalar.getType(scalar.getScalarType())); if (simpleType == null) { parameterTypes[i++] = Scalar.getType(scalar.getScalarType()); } else { parameterTypes[i++] = simpleType.getTypeName(); } continue; } if (formalParameter.getDefaultValue() != null && formalParameter.getDefaultValue() instanceof ArrayCreation) { parameterTypes[i++] = PHPSimpleTypes.ARRAY.getTypeName(); continue; } parameterTypes[i++] = UNKNOWN_TYPE; } } StringBuilder returnTypeBuffer = new StringBuilder(); if (null != resolvedBinding) { // check if the return type has already been determined by the // function/method signature (PHP 7) if (retType == null) { // no return type in the signature - try to evaluate it returnTypes = resolvedBinding.getReturnType(); if (null != returnTypes && returnTypes.length > 0) { List<ITypeBinding> returnTypesList = removeDuplicateTypes(returnTypes); for (ITypeBinding returnType : returnTypesList) { if (returnType.isUnknown()) { // show unknown types as if they were null types, // even if looking for returnType.isUnknown() is not // the same as looking for returnType.isNullType() returnTypeBuffer.append(PHPSimpleTypes.NULL.getTypeName()) .append(Constants.TYPE_SEPARATOR_CHAR); } else if (returnType.isAmbiguous()) { returnTypeBuffer.append("Ambiguous").append(Constants.TYPE_SEPARATOR_CHAR); //$NON-NLS-1$ } else if (!appendAllPossibleTypes(returnType.getEvaluatedType(), returnTypeBuffer)) { returnTypeBuffer.append(returnType.getName()).append(Constants.TYPE_SEPARATOR_CHAR); } } if (returnTypeBuffer.length() > 0) { retType = returnTypeBuffer.substring(0, returnTypeBuffer.length() - 1); } } } typeParametersTypes = resolvedBinding.getParameterTypes(); if (null != typeParametersTypes) { int i = 0; typeParameterNames = new String[typeParametersTypes.length]; for (ITypeBinding type : typeParametersTypes) { typeParameterNames[i++] = type.getName(); } } } String[] paramNames = method.getParameterNames(); if (formalParameters == null) { parameterTypes = new String[paramNames.length]; for (int i = 0; i < paramNames.length; i++) { parameterTypes[i] = UNKNOWN_TYPE; } } else { for (int i = 0; i < formalParameters.size(); i++) { if (formalParameters.get(i).isVariadic()) { paramNames[i] = ScriptElementLabels.ELLIPSIS_STRING + paramNames[i]; } } } // add parameter type before parameter name for (int i = 0; i < paramNames.length; i++) { if (null != parameterTypes && null != parameterTypes[i]) { paramNames[i] = parameterTypes[i] + " " + paramNames[i]; //$NON-NLS-1$ // } else { // String parameterType = // detectFromHungarianNotation(paramNames[i]); // if (parameterType != null) { // paramNames[i] = parameterType + " " + paramNames[i]; // } } } IType declaringType = method.getDeclaringType(); if (null != declaringType) { return StubUtility.getMethodComment(method.getScriptProject(), declaringType.getElementName(), method.getElementName(), paramNames, retType, typeParameterNames, overridden, false, lineDelimiter, newExceptions); } return StubUtility.getMethodComment(method.getScriptProject(), null, method.getElementName(), paramNames, retType, typeParameterNames, overridden, false, lineDelimiter, newExceptions); } // XXX: make it private again once 438661 is fixed public static Program generateProgram(IMember member, Program program) { ISourceModule source = member.getSourceModule(); ASTParser parserForExpected = ASTParser .newParser(ProjectOptions.getPHPVersion(source.getScriptProject().getProject()), source); try { parserForExpected.setSource(source); program = parserForExpected.createAST(new NullProgressMonitor()); program.recordModifications(); program.setSourceModule(source); } catch (Exception e) { } return program; } private static List<ITypeBinding> removeDuplicateTypes(ITypeBinding[] returnTypes) { List<ITypeBinding> types = new ArrayList<>(); for (ITypeBinding type : returnTypes) { if (!types.contains(type)) { types.add(type); } } return types; } /** * Checks if the parameter "type" is a type container object (whose class is * AmbiguousType or MultiTypeType) and prints its content recursively in * "buffer". If "type" is not a type container object, no data will be * printed in "buffer" and false will be returned. * * When a (non-empty) type container is printed, last printed character will * always be '|'. * * @param type * @param buffer * @return true if "type" is a type container object, false otherwise */ private static boolean appendAllPossibleTypes(IEvaluatedType type, StringBuilder buffer) { List<String> foundTypes = new ArrayList<>(); if (findAllPossibleTypes(type, foundTypes, 0, true)) { for (String foundType : foundTypes) { buffer.append(foundType).append(Constants.TYPE_SEPARATOR_CHAR); } return true; } return false; } private static boolean findAllPossibleTypes(IEvaluatedType type, List<String> foundTypes, int level, boolean firstCall) { if (type instanceof AmbiguousType) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=467148 IEvaluatedType[] allPossibleTypes = ((AmbiguousType) type).getPossibleTypes(); // XXX: allPossibleTypes should not be empty for (IEvaluatedType possibleType : allPossibleTypes) { findAllPossibleTypes(possibleType, foundTypes, level, false); } return true; } else if (type instanceof MultiTypeType) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=467151 List<IEvaluatedType> allPossibleTypes = ((MultiTypeType) type).getTypes(); // empty MultiTypeTypes correspond to "array()" occurrences or // "(array)" value casting occurrences if (!allPossibleTypes.isEmpty()) { for (IEvaluatedType possibleType : allPossibleTypes) { findAllPossibleTypes(possibleType, foundTypes, level + 1, false); } return true; } } if (!firstCall || type instanceof MultiTypeType) { StringBuilder buffer = new StringBuilder(); if (type instanceof MultiTypeType) { buffer.append(PHPSimpleTypes.ARRAY.getTypeName()); } else { buffer.append(type.getTypeName()); } for (int i = 1; i <= level; i++) { buffer.append(PHPEvaluationUtils.BRACKETS); } String foundType = buffer.toString(); // do not insert duplicates if (!foundTypes.contains(foundType)) { foundTypes.add(foundType); } return true; } return false; } // /** // * Detect variable type from variable named using Hungarian notation // */ // private static String detectFromHungarianNotation(String paramName) { // if (paramName.matches("\\$ch[A-Z].*")) { // return "char"; // } // if (paramName.matches("\\$ar[A-Z].*")) { // return PHPSimpleTypes.ARRAY.getTypeName(); // } // if (paramName.matches("\\$str[A-Z].*")) { // return PHPSimpleTypes.STRING.getTypeName(); // } // if (paramName.matches("\\$fl[A-Z].*")) { // return "float"; // } // if (paramName.matches("\\$n[A-Z].*")) { // return "integer"; // } // if (paramName.matches("\\$b[A-Z].*")) { // return PHPSimpleTypes.BOOLEAN.getTypeName(); // } // return null; // } /** * Returns the comment for a method or constructor using the comment code * templates (constructor / method / overriding method). <code>null</code> * is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * * @param decl * The MethodDeclaration AST node that will be added as new * method. The node does not need to exist in an AST (no parent * needed) and does not need to resolve. See * {@link org.eclipse.jdt.core.dom.AST#newMethodDeclaration()} * for how to create such a node. * @param isDeprecated * If set, the method is deprecated * @param overriddenMethodName * If a method is overridden, the simple name of the overridden * method, or <code>null</code> if no method is overridden. * @param overriddenMethodDeclaringTypeName * If a method is overridden, the fully qualified type name of * the overridden method's declaring type, or <code>null</code> * if no method is overridden. * @param overriddenMethodParameterTypeNames * If a method is overridden, the fully qualified parameter type * names of the overridden method, or <code>null</code> if no * method is overridden. * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed comment or <code>null</code> if the * comment code template is empty. The returned string is * unformatted and and has no indent (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.2 */ // public static String getMethodComment(IScriptProject sp, String // declaringTypeName, MethodDeclaration decl, boolean isDeprecated, String // overriddenMethodName, String overriddenMethodDeclaringTypeName, String[] // overriddenMethodParameterTypeNames, String lineDelimiter) throws // CoreException { // return StubUtility.getMethodComment(sp, declaringTypeName, decl, // isDeprecated, overriddenMethodName, overriddenMethodDeclaringTypeName, // overriddenMethodParameterTypeNames, false, lineDelimiter); // } /** * Returns the content of the body for a method or constructor using the * method body templates. <code>null</code> is returned if the template is * empty. * <p> * The returned string is unformatted and not indented. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * Name of the method. * @param isConstructor * Defines if the created body is for a constructor. * @param bodyStatement * The code to be entered at the place of the variable * ${body_statement}. * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed body content or <code>null</code> if the * comment code template is empty. The returned string is * unformatted and and has no indent (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. */ public static String getMethodBodyContent(IScriptProject sp, String declaringTypeName, String methodName, boolean isConstructor, String bodyStatement, String lineDelimiter) throws CoreException { return StubUtility.getMethodBodyContent(isConstructor, sp, declaringTypeName, methodName, bodyStatement, lineDelimiter); } /** * Returns the content of body for a getter method using the getter method * body template. <code>null</code> is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * The name of the getter method. * @param fieldName * The name of the field to get in the getter method, * corresponding to the template variable for ${field}. * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed body content or <code>null</code> if the * comment code template is empty. The returned string is * unformatted and and has no indent (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.0 */ public static String getGetterMethodBodyContent(IScriptProject sp, String declaringTypeName, String methodName, String fieldName, String lineDelimiter) throws CoreException { return StubUtility.getGetterMethodBodyContent(sp, declaringTypeName, methodName, fieldName, lineDelimiter); } /** * Returns the content of body for a setter method using the setter method * body template. <code>null</code> is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * The name of the setter method. * @param fieldName * The name of the field to be set in the setter method, * corresponding to the template variable for ${field}. * @param paramName * The name of the parameter passed to the setter method, * corresponding to the template variable for $(param). * @param lineDelimiter * The line delimiter to be used. * @return Returns the constructed body content or <code>null</code> if the * comment code template is empty. The returned string is * unformatted and and has no indent (formatting required). * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.0 */ public static String getSetterMethodBodyContent(IScriptProject sp, String declaringTypeName, String methodName, String fieldName, String paramName, String lineDelimiter) throws CoreException { return StubUtility.getSetterMethodBodyContent(sp, declaringTypeName, methodName, fieldName, paramName, lineDelimiter); } /** * Returns the comment for a getter method using the getter comment * template. <code>null</code> is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * Name of the method. * @param fieldName * Name of the field to get. * @param fieldType * The type of the field to get. * @param bareFieldName * The field name without prefix or suffix. * @param lineDelimiter * The line delimiter to be used. * @return Returns the generated getter comment or <code>null</code> if the * code template is empty. The returned content is not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.0 */ public static String getGetterComment(IScriptProject sp, String declaringTypeName, String methodName, String fieldName, String fieldType, String bareFieldName, String lineDelimiter) throws CoreException { return StubUtility.getGetterComment(sp, declaringTypeName, methodName, fieldName, fieldType, bareFieldName, lineDelimiter); } /** * Returns the comment for a setter method using the setter method body * template. <code>null</code> is returned if the template is empty. * <p> * The returned string is unformatted and not indented. * * @param sp * The compilation unit to which the method belongs. The * compilation unit does not need to exist. * @param declaringTypeName * Name of the type to which the method belongs. For inner types * the name must be qualified and include the outer types names * (dot separated). See * {@link org.eclipse.jdt.core.IType#getTypeQualifiedName(char)}. * @param methodName * Name of the method. * @param fieldName * Name of the field that is set. * @param fieldType * The type of the field that is to set. * @param paramName * The name of the parameter that used to set. * @param bareFieldName * The field name without prefix or suffix. * @param lineDelimiter * The line delimiter to be used. * @return Returns the generated setter comment or <code>null</code> if the * code template is empty. The returned comment is not indented. * @throws CoreException * Thrown when the evaluation of the code template fails. * @since 3.0 */ public static String getSetterComment(IScriptProject sp, String declaringTypeName, String methodName, String fieldName, String fieldType, String paramName, String bareFieldName, String lineDelimiter) throws CoreException { return StubUtility.getSetterComment(sp, declaringTypeName, methodName, fieldName, fieldType, paramName, bareFieldName, lineDelimiter); } }