/******************************************************************************* * Copyright (c) 2017 Rogue Wave Software Inc. 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: * Rogue Wave Software Inc. - initial implementation *******************************************************************************/ package org.eclipse.php.ui.util; import java.io.Reader; import java.io.StringReader; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.core.*; import org.eclipse.dltk.evaluation.types.UnknownType; import org.eclipse.dltk.ti.types.IEvaluatedType; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.core.ast.visitor.AbstractVisitor; import org.eclipse.php.core.compiler.PHPFlags; import org.eclipse.php.core.project.ProjectOptions; import org.eclipse.php.internal.core.ast.rewrite.ASTRewrite; import org.eclipse.php.internal.core.ast.rewrite.ListRewrite; import org.eclipse.php.internal.core.ast.scanner.php5.PHPAstLexer; import org.eclipse.php.internal.core.typeinference.PHPClassType; import org.eclipse.php.internal.core.typeinference.PHPModelUtils; import org.eclipse.php.internal.core.typeinference.PHPTypeInferencer; import org.eclipse.php.internal.core.typeinference.context.FileContext; import org.eclipse.php.internal.core.typeinference.context.TypeContext; import org.eclipse.php.internal.core.typeinference.goals.ClassVariableDeclarationGoal; import org.eclipse.php.internal.core.typeinference.goals.phpdoc.PHPDocClassVariableGoal; import org.eclipse.php.internal.ui.PHPUiPlugin; import org.eclipse.php.internal.ui.actions.CodeGenerationSettings; import org.eclipse.php.internal.ui.corext.codemanipulation.StubUtility; import org.eclipse.php.internal.ui.editor.PHPStructuredEditor; import org.eclipse.php.ui.CodeGeneration; import org.eclipse.swt.graphics.Point; import org.eclipse.ui.*; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.ITextEditor; public class CodeGenerationUtils { private CodeGenerationUtils() { } /** * Finds a method in a type. This searches for a method with the same name * and signature. Parameter types are only compared by the simple name, no * resolving for the fully qualified type name is done. Constructors are * only compared by parameters, not the name. * * @param name * The name of the method to find * @param paramTypes * The type signatures of the parameters e.g. * <code>{"QString;","I"}</code> * @param isConstructor * If the method is a constructor * @param type * the type * @return The first found method or <code>null</code>, if nothing foun * @throws ModelException * thrown when the type can not be accessed */ public static IMethod findMethod(String name, int parameters, boolean isConstructor, IType type) throws ModelException { IMethod[] methods = type.getMethods(); for (IMethod method : methods) { if (isSameMethodSignature(name, parameters, isConstructor, method)) { return method; } } return null; } /** * Tests if a method equals to the given signature. Parameter types are only * compared by the simple name, no resolving for the fully qualified type * name is done. Constructors are only compared by parameters, not the name. * * @param name * Name of the method * @param isConstructor * Specifies if the method is a constructor * @param curr * the method * @return Returns <code>true</code> if the method has the given name and * parameter types and constructor state. * @throws ModelException * thrown when the method can not be accessed */ public static boolean isSameMethodSignature(String name, int parameters, boolean isConstructor, IMethod curr) throws ModelException { if (isConstructor || name.equals(curr.getElementName())) { if (isConstructor == curr.isConstructor()) { String[] currParamTypes = curr.getParameterNames(); if (parameters == currParamTypes.length) { return true; } } } return false; } /** * Returns the Setter method of the field. * * @param field * @return getter method * @throws ModelException */ public static IMethod getSetter(IField field) throws ModelException { return findMethod(getSetterName(field), 1, false, field.getDeclaringType()); } /** * Returns the getter name String. * * @param field * @return Getter name string * @throws ModelException */ public static String getSetterName(IField field) throws ModelException { return "set" + toInitialCaps(field.getElementName(), true); //$NON-NLS-1$ } /** * utility method for changing the case of a string's first letter * * @param s * input string * @param toUpper * if true change first letter to upper case, false change to * lower case * @return the given string with the first letter changed to upper/lower * case - according to the flag. Empty string for null or empty * strings. */ private static String toInitialCaps(String label, boolean toUpper) { String s = label; if (s == null || s.length() == 0) { return ""; //$NON-NLS-1$ } // remove the $ from PHP variable. s = s.substring(1); // strip leading underscore to make getter/setter names more // reader-friendly if ((s.charAt(0) == '_') && (s.length() > 1) && (s.charAt(1) != '_')) { s = s.substring(1); } StringBuilder res = new StringBuilder(); if (toUpper) { res.append(Character.toUpperCase(s.charAt(0))); } else { res.append(Character.toLowerCase(s.charAt(0))); } if (s.length() > 1) { res.append(s.substring(1)); } return res.toString(); } /** * Returns the getter method of give filed. * * @param field * @return the getter method. * @throws ModelException */ public static IMethod getGetter(IField field) throws ModelException { String getterName = getGetterName(field); return findMethod(getterName, 0, false, field.getDeclaringType()); } /** * Returns the getter name string. * * @param field * @return getter name string. */ public static String getGetterName(IField field) { return "get" + toInitialCaps(field.getElementName(), true); //$NON-NLS-1$ } /** * Create a stub for a getter of the given field using getter/setter * templates. The resulting code has to be formatted and indented. * * @param field * The field to create a getter for * @param setterName * The chosen name for the setter * @param addComments * If <code>true</code>, comments will be added. * @param flags * The flags signaling visibility, if static, synchronized or * final * @return Returns the generated stub. * @throws CoreException * when stub creation failed */ public static void createSetterStub(IField field, String setterName, boolean addComments, int flags, ListRewrite rewrite, ASTNode insertion) throws CoreException { String fieldName = field.getElementName(); IType parentType = field.getDeclaringType(); AST ast = rewrite.getASTRewrite().getAST(); String lineDelim = StubUtility.getLineDelimiterUsed(field.getScriptProject()); MethodDeclaration method = new MethodDeclarationStub(ast); FunctionDeclaration func = ast.newFunctionDeclaration(); FormalParameter param = ast.newFormalParameter(); Expression exp = ast.newScalar(fieldName); param.setParameterName(exp); func.formalParameters().add(param); func.setFunctionName(rewrite.getParent().getAST().newIdentifier(getSetterName(field))); method.setFunction(func); boolean isStatic = Flags.isStatic(flags); boolean isFinal = Flags.isFinal(flags); String argname = field.getElementName(); if (argname.equals(fieldName) || (!isStatic)) { if (isStatic) { fieldName = parentType.getElementName() + "::" + fieldName; //$NON-NLS-1$ } else { fieldName = "$this->" + removeDollarSign(fieldName); //$NON-NLS-1$ } } // only php 5 supports modifers. int modifiers = 0; if (isStatic) { modifiers = modifiers | Modifiers.AccStatic; } if (isFinal) { modifiers = modifiers | Modifiers.AccFinal; } if (Flags.isPublic(flags)) { modifiers = modifiers | Modifiers.AccPublic; } if (Flags.isProtected(flags)) { modifiers = modifiers | Modifiers.AccProtected; } if (Flags.isPrivate(flags)) { modifiers = modifiers | Modifiers.AccPrivate; } method.setModifier(modifiers); Block body = ast.newBlock(); func.setBody(body); String bodyContent = CodeGeneration.getSetterMethodBodyContent(field.getScriptProject(), parentType.getTypeQualifiedName(), setterName, fieldName, field.getElementName(), lineDelim); if (bodyContent != null) { ASTNode todoNode = rewrite.getASTRewrite().createStringPlaceholder(bodyContent, ASTNode.EMPTY_STATEMENT); body.statements().add((Statement) todoNode); } if (addComments) { String filedType = getFieldType(field); String comment = CodeGeneration.getSetterComment(field.getScriptProject(), field.getDeclaringType().getElementName(), setterName, fieldName, filedType, field.getElementName(), field.getElementName(), lineDelim); if (comment != null) { Comment commentNode = (Comment) rewrite.getASTRewrite().createStringPlaceholder(comment + lineDelim, ASTNode.COMMENT); commentNode.setCommentType(Comment.TYPE_PHPDOC); method.setComment(commentNode); } } addNewAccessor(method, rewrite, insertion); } private static String getFieldType(IField field) throws ModelException { String filedType = field.getType(); IType type = field.getDeclaringType(); PHPClassType classType = PHPClassType.fromIType(type); ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(type.getSourceModule(), null); FileContext fileContext = new FileContext(type.getSourceModule(), moduleDeclaration, field.getNameRange().getOffset()); TypeContext typeContext = new TypeContext(fileContext, classType); PHPTypeInferencer typeInferencer = new PHPTypeInferencer(); PHPDocClassVariableGoal phpDocGoal = new PHPDocClassVariableGoal(typeContext, field.getElementName(), field.getNameRange().getOffset()); IEvaluatedType evaluatedType = typeInferencer.evaluateTypePHPDoc(phpDocGoal, 3000); if (evaluatedType instanceof UnknownType) { ClassVariableDeclarationGoal goal = new ClassVariableDeclarationGoal(typeContext, new IType[] { type }, field.getElementName()); evaluatedType = typeInferencer.evaluateType(goal); } if (!(evaluatedType instanceof UnknownType)) { filedType = evaluatedType.getTypeName(); } return filedType; } private static String removeDollarSign(String fieldName) { String name = fieldName; if (isDollared(name)) { name = name.substring(1); } return name; } private static boolean isDollared(String variableName) { return variableName.indexOf('$') == 0; } /** * Adds a new accessor for the specified field. * * @param type * the type * @param field * the field * @param contents * the contents of the accessor method * @param rewrite * the list rewrite to use * @param insertion * the insertion point * @throws JavaModelException * if an error occurs */ private static void addNewAccessor(ASTNode node, final ListRewrite rewrite, final ASTNode insertion) throws ModelException { if (insertion != null) { rewrite.insertBefore(node, insertion, null); } else { rewrite.insertLast(node, null); } } /** * Create a stub for a getter of the given field using getter/setter * templates. The resulting code has to be formatted and indented. * * @param field * The field to create a getter for * @param getterName * The chosen name for the getter * @param addComments * If <code>true</code>, comments will be added. * @param flags * The flags signaling visibility, if static, synchronized or * final * @param insertion * @param rewrite * @param field2 * @param type * @return Returns the generated stub. * @throws CoreException * when stub creation failed */ public static void createGetterStub(IField field, String getterName, boolean addComments, int flags, IType type, ListRewrite rewrite, ASTNode insertion) throws CoreException { String fieldName = field.getElementName(); IType parentType = field.getDeclaringType(); String lineDelim = StubUtility.getLineDelimiterUsed(field.getScriptProject()); AST ast = rewrite.getASTRewrite().getAST(); MethodDeclaration method = new MethodDeclarationStub(ast); FunctionDeclaration func = ast.newFunctionDeclaration(); func.setFunctionName(ast.newIdentifier(getGetterName(field))); method.setFunction(func); boolean isStatic = Flags.isStatic(flags); boolean isFinal = Flags.isFinal(flags); fieldName = isStatic ? parentType.getElementName() + "::" + fieldName : "$this->" + removeDollarSign(fieldName); //$NON-NLS-1$ int modifiers = 0; if (isStatic) { modifiers = modifiers | Modifiers.AccStatic; } if (isFinal) { modifiers = modifiers | Modifiers.AccFinal; } if (Flags.isPublic(flags)) { modifiers = modifiers | Modifiers.AccPublic; } if (Flags.isProtected(flags)) { modifiers = modifiers | Modifiers.AccProtected; } if (Flags.isPrivate(flags)) { modifiers = modifiers | Modifiers.AccPrivate; } method.setModifier(modifiers); Block body = ast.newBlock(); func.setBody(body); String bodyContent = CodeGeneration.getGetterMethodBodyContent(field.getScriptProject(), parentType.getElementName(), getterName, fieldName, lineDelim); if (bodyContent != null) { ASTNode todoNode = rewrite.getASTRewrite().createStringPlaceholder(bodyContent, ASTNode.EMPTY_STATEMENT); body.statements().add((Statement) todoNode); } if (addComments) { String filedType = getFieldType(field); String comment = CodeGeneration.getGetterComment(field.getScriptProject(), field.getDeclaringType().getElementName(), getterName, fieldName, filedType, field.getElementName(), lineDelim); if (comment != null) { Comment commentNode = (Comment) rewrite.getASTRewrite().createStringPlaceholder(comment + lineDelim, ASTNode.COMMENT); commentNode.setCommentType(Comment.TYPE_PHPDOC); method.setComment(commentNode); } } addNewAccessor(method, rewrite, insertion); } /** * Returns the element after the give element. * * @param member * a Java element * @return the next sibling of the given element or <code>null</code> * @throws ModelException * thrown if the element could not be accessed */ public static IModelElement findNextSibling(IModelElement member) throws ModelException { IModelElement parent = member.getParent(); if (parent instanceof IParent) { IModelElement[] elements = ((IParent) parent).getChildren(); for (int i = elements.length - 2; i >= 0; i--) { if (member.equals(elements[i])) { return elements[i + 1]; } } } return null; } /** * Evaluates the insertion position of a new node. * * @param listRewrite * The list rewriter to which the new node will be added * @param sibling * The Java element before which the new element should be added. * @return the AST node of the list to insert before or null to insert as * last. * @throws ModelException * thrown if accessing the Model element failed */ public static ASTNode getNodeToInsertBefore(ListRewrite listRewrite, IModelElement sibling) throws ModelException { if (sibling instanceof IMember) { ISourceRange sourceRange = ((IMember) sibling).getSourceRange(); if (sourceRange == null) { return null; } int insertPos = sourceRange.getOffset(); return getNodeToInsertBefore(listRewrite, insertPos); } return null; } /** * Evaluates the insertion position of a new node. * * @param listRewrite * The list rewriter to which the new node will be added * @param insertPos * The position of the element. * @return the AST node of the list to insert before or null to insert as * last. * @throws ModelException * thrown if accessing the Model element failed */ public static ASTNode getNodeToInsertBefore(ListRewrite listRewrite, int insertPos) { List<?> members = listRewrite.getOriginalList(); for (Object object : members) { ASTNode curr = (ASTNode) object; if (curr.getStart() >= insertPos) { return curr; } } return null; } /** * Returns the current model element from the PHP editor * * @param source * @param editor * @param selection2 * @return * @throws ModelException */ public static IModelElement getCurrentModelElement(IModelElement source, PHPStructuredEditor editor, ITextSelection selection) throws ExecutionException { ISourceViewer viewer = editor.getTextViewer(); Point originalSelection = viewer.getSelectedRange(); int offset = -1; if (originalSelection == null) { if (selection instanceof TextSelection) { TextSelection textSelection = (TextSelection) selection; offset = textSelection.getOffset(); } } else { offset = originalSelection.x; } if (source instanceof ISourceModule) { ISourceModule module = (ISourceModule) source; try { return module.getElementAt(offset); } catch (ModelException e) { throw new ExecutionException("Error trying to resolve document's element", e); //$NON-NLS-1$ } } return null; } public static PHPStructuredEditor getPHPEditor(ExecutionEvent event) { IEditorPart editorPart = HandlerUtil.getActiveEditor(event); PHPStructuredEditor textEditor = null; if (editorPart instanceof PHPStructuredEditor) { textEditor = (PHPStructuredEditor) editorPart; } else { Object o = editorPart.getAdapter(ITextEditor.class); if (o != null) { textEditor = (PHPStructuredEditor) o; } } return textEditor; } public static IMethodBinding[] getOverridableMethods(AST ast, ITypeBinding typeBinding, boolean isSubType) { List<IMethodBinding> allMethods = new ArrayList<>(); IMethodBinding[] typeMethods = typeBinding.getDeclaredMethods(); for (IMethodBinding methodBinding : typeMethods) { final int modifiers = methodBinding.getModifiers(); if (!Flags.isPrivate(modifiers)) { allMethods.add(methodBinding); } } ITypeBinding clazz = typeBinding.getSuperclass(); while (clazz != null) { IMethodBinding[] methods = clazz.getDeclaredMethods(); for (IMethodBinding methodBinding : methods) { final int modifiers = methodBinding.getModifiers(); if (!Flags.isPrivate(modifiers) && findOverridingMethod(methodBinding, allMethods) == null) { allMethods.add(methodBinding); } } clazz = clazz.getSuperclass(); } clazz = typeBinding; while (clazz != null) { ITypeBinding[] superInterfaces = clazz.getInterfaces(); for (ITypeBinding superInterface : superInterfaces) { getOverridableMethods(ast, superInterface, allMethods); } clazz = clazz.getSuperclass(); } if (!isSubType) { allMethods.removeAll(Arrays.asList(typeMethods)); } if (!typeBinding.isInterface()) { for (int index = allMethods.size() - 1; index >= 0; index--) { IMethodBinding method = allMethods.get(index); int modifiers = method.getModifiers(); if (PHPFlags.isFinal(modifiers)) { allMethods.remove(index); } } } return allMethods.toArray(new IMethodBinding[allMethods.size()]); } private static IMethodBinding findOverridingMethod(IMethodBinding method, List<IMethodBinding> allMethods) { for (IMethodBinding curr : allMethods) { if (Bindings.isSubsignature(curr, method)) { return curr; } } return null; } private static void getOverridableMethods(AST ast, ITypeBinding superBinding, List<IMethodBinding> allMethods) { IMethodBinding[] methods = superBinding.getDeclaredMethods(); for (IMethodBinding method : methods) { final int modifiers = method.getModifiers(); if (!method.isConstructor() && !PHPFlags.isPrivate(modifiers) && findOverridingMethod(method, allMethods) == null) { allMethods.add(method); } } ITypeBinding[] superInterfaces = superBinding.getInterfaces(); for (ITypeBinding superInterface : superInterfaces) { getOverridableMethods(ast, superInterface, allMethods); } } /** * Returns the array of unimplemented methods of given type. * * @param typeBinding * @return the array of unimplemented methods */ public static IMethodBinding[] getUnimplementedMethods(ITypeBinding typeBinding) { return getUnimplementedMethods(typeBinding, false); } /** * The array of unimplemented methods of given type. * * @param typeBinding * @param implementAbstractsOfInput * weather the abstract method should be included. * @return the array of unimplemented methods */ public static IMethodBinding[] getUnimplementedMethods(ITypeBinding typeBinding, boolean implementAbstractsOfInput) { List<IMethodBinding> allMethods = new ArrayList<>(); List<IMethodBinding> toImplement = new ArrayList<>(); IMethodBinding[] typeMethods = typeBinding.getDeclaredMethods(); for (IMethodBinding typeMethod : typeMethods) { int modifiers = typeMethod.getModifiers(); if (!typeMethod.isConstructor() && !PHPFlags.isStatic(modifiers) && !PHPFlags.isPrivate(modifiers)) { allMethods.add(typeMethod); } } ITypeBinding superClass = typeBinding.getSuperclass(); Set<ITypeBinding> bindingSet = new HashSet<>(); while (superClass != null && !bindingSet.contains(superClass)) { bindingSet.add(superClass); typeMethods = superClass.getDeclaredMethods(); for (IMethodBinding curr : typeMethods) { int modifiers = curr.getModifiers(); if (!curr.isConstructor() && !PHPFlags.isStatic(modifiers) && !PHPFlags.isPrivate(modifiers) && findMethodBinding(curr, allMethods) == null) { allMethods.add(curr); } } superClass = superClass.getSuperclass(); } for (IMethodBinding curr : allMethods) { int modifiers = curr.getModifiers(); if ((PHPFlags.isAbstract(modifiers) || curr.getDeclaringClass().isInterface()) && (implementAbstractsOfInput || typeBinding != curr.getDeclaringClass())) { // implement all abstract methods toImplement.add(curr); } } Set<ITypeBinding> visited = new HashSet<>(); ITypeBinding curr = typeBinding; bindingSet.clear(); while (curr != null && !bindingSet.contains(curr)) { bindingSet.add(curr); ITypeBinding[] superInterfaces = curr.getInterfaces(); for (ITypeBinding superInterface : superInterfaces) { findUnimplementedInterfaceMethods(superInterface, visited, allMethods, toImplement); } curr = curr.getSuperclass(); } return toImplement.toArray(new IMethodBinding[toImplement.size()]); } private static void findUnimplementedInterfaceMethods(ITypeBinding typeBinding, Set<ITypeBinding> visited, List<IMethodBinding> allMethods, List<IMethodBinding> toImplement) { if (visited.add(typeBinding)) { IMethodBinding[] typeMethods = typeBinding.getDeclaredMethods(); for (IMethodBinding curr : typeMethods) { IMethodBinding impl = findMethodBinding(curr, allMethods); if (impl == null || !Bindings.isVisibleInHierarchy(impl)) { if (impl != null) { allMethods.remove(impl); } toImplement.add(curr); allMethods.add(curr); } } ITypeBinding[] superInterfaces = typeBinding.getInterfaces(); for (ITypeBinding superInterface : superInterfaces) { findUnimplementedInterfaceMethods(superInterface, visited, allMethods, toImplement); } } } private static IMethodBinding findMethodBinding(IMethodBinding method, List<IMethodBinding> allMethods) { for (IMethodBinding curr : allMethods) { if (Bindings.isSubsignature(method, curr)) { return curr; } } return null; } private static int getImplementationModifiers(IMethodBinding method, boolean deferred) { int modifiers = method.getModifiers() & ~Modifiers.AccPrivate; modifiers = modifiers & ~Modifiers.AccAbstract; if (deferred) { modifiers = modifiers & ~Modifiers.AccProtected; modifiers = modifiers | Modifiers.AccPublic; } return modifiers; } /** * Creates the method stub of the given method. * * @param unit * @param rewrite * @param binding * @param type * @param settings * @param deferred * @return the block of the method. * @throws CoreException */ public static MethodDeclaration createImplementationStub(Program unit, ASTRewrite rewrite, IMethodBinding binding, String type, CodeGenerationSettings settings, boolean deferred) throws CoreException { AST ast = rewrite.getAST(); MethodDeclaration decl = new MethodDeclarationStub(ast); decl.setModifier(getImplementationModifiers(binding, deferred)); FunctionDeclaration func = ast.newFunctionDeclaration(); func.setFunctionName(ast.newIdentifier(binding.getName())); func.setFlags(getImplementationModifiers(binding, deferred)); decl.setFunction(func); IMethod method = (IMethod) ((MethodBinding) binding).getPHPElement(); MethodDeclaration methodDecl = null; if (method.getSourceModule() != unit.getSourceModule()) { Program program = null; ISourceModule source = method.getSourceModule(); IProject project = source.getScriptProject().getProject(); ASTParser parserForExpected = ASTParser.newParser(ProjectOptions.getPHPVersion(project), ProjectOptions.useShortTags(project)); try { parserForExpected.setSource(source); program = parserForExpected.createAST(new NullProgressMonitor()); program.recordModifications(); program.setSourceModule(source); ASTNode function = program.getElementAt(method.getSourceRange().getOffset()); if (function instanceof FunctionDeclaration) { methodDecl = (MethodDeclaration) function.getParent(); } if (function instanceof MethodDeclaration) { methodDecl = (MethodDeclaration) function; } } catch (Exception e) { PHPUiPlugin.log(e); } } else { ASTNode function = unit.getElementAt(method.getSourceRange().getOffset()); if (function instanceof FunctionDeclaration) { methodDecl = (MethodDeclaration) function.getParent(); } if (function instanceof MethodDeclaration) { methodDecl = (MethodDeclaration) function; } } List<FormalParameter> typeParams = null; List<FormalParameter> typeParameters = null; if (methodDecl != null) { func.setReturnType(ASTNode.copySubtree(ast, methodDecl.getFunction().getReturnType())); typeParams = methodDecl.getFunction().formalParameters(); typeParameters = func.formalParameters(); for (int i = 0; typeParams != null && i < typeParams.size(); i++) { FormalParameter curr = typeParams.get(i); FormalParameter newTypeParam = ast.newFormalParameter(); Expression exp = ASTNode.copySubtree(ast, curr.getParameterName()); newTypeParam.setParameterName(exp); newTypeParam.setIsVariadic(curr.isVariadic()); Expression value = curr.getDefaultValue(); if (value != null) { newTypeParam.setDefaultValue(ASTNode.copySubtree(ast, value)); } Expression ptype = curr.getParameterType(); if (ptype != null) { Expression expression = ASTNode.copySubtree(ast, ptype); if (ptype instanceof NamespaceName) { NamespaceName newName = (NamespaceName) expression; if (!newName.isGlobal()) { String fullName = PHPModelUtils.getFullName(newName); fullName = PHPModelUtils.getFullName(fullName, method.getSourceModule(), newName.getStart()); String[] names = fullName.split("\\\\"); //$NON-NLS-1$ if (names.length > 1) { newName.segments().clear(); newName.setGlobal(true); for (int j = 0; j < names.length; j++) { Identifier identifier = ast.newIdentifier(); identifier.setName(names[j]); newName.segments().add(identifier); } } } } newTypeParam.setParameterType(expression); } typeParameters.add(newTypeParam); } } String delimiter = StubUtility.getLineDelimiterUsed(unit.getSourceModule().getScriptProject()); if (!deferred) { Block body = ast.newBlock(); func.setBody(body); String bodyStatement = ""; //$NON-NLS-1$ String placeHolder = CodeGeneration.getMethodBodyContent(unit.getSourceModule().getScriptProject(), type, binding.getName(), false, bodyStatement, delimiter); if (placeHolder != null) { ASTNode todoNode = rewrite.createStringPlaceholder(placeHolder, ASTNode.EMPTY_STATEMENT); body.statements().add((Statement) todoNode); if (addReturnStatement(methodDecl)) { Identifier parentIdentifier = ast.newIdentifier("parent"); FunctionName functionName = ast.newFunctionName(ast.newIdentifier(binding.getName())); FunctionInvocation functionInvocation = ast.newFunctionInvocation(functionName, null); Expression expression = ast.newStaticMethodInvocation(parentIdentifier, functionInvocation); body.statements().add(ast.newReturnStatement(expression)); } } } if (settings != null && settings.createComments) { String comment = CodeGeneration.getMethodComment(method, method, delimiter); if (comment != null) { Comment phpdoc = (Comment) rewrite.createStringPlaceholder(comment + delimiter, ASTNode.COMMENT); phpdoc.setCommentType(Comment.TYPE_PHPDOC); decl.setComment(phpdoc); } } return decl; } private static boolean addReturnStatement(MethodDeclaration methodDecl) { if (methodDecl == null || methodDecl.getFunction() == null || methodDecl.getFunction().getBody() == null) { return false; } final AtomicBoolean add = new AtomicBoolean(false); methodDecl.getFunction().getBody().accept(new AbstractVisitor() { @Override public boolean visit(ReturnStatement expressionStatement) { add.set(true); return false; } }); return add.get(); } /** * Opens the editor currently associated with the given element * (IJavaElement, IFile, IStorage...) * * @param inputElement * the input element * @param activate * <code>true</code> if the editor should be activated * @return an open editor or <code>null</code> if an external editor was * opened * @throws PartInitException * if the editor could not be opened or the input element is not * valid Status code if opening the editor failed as no editor * input could be created for the given element. */ public static IEditorPart openInEditor(IModelElement inputElement, boolean activate) throws PartInitException { if (inputElement instanceof IField) { IWorkbenchPage page = PHPUiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage(); IEditorPart editor = page != null ? page.getActiveEditor() : null; if (editor != null && editor instanceof PHPStructuredEditor) { IModelElement source = ((PHPStructuredEditor) editor).getModelElement(); if (source.equals(((IField) inputElement).getSourceModule())) { if (activate && (page != null && page.getActivePart() != editor)) { page.activate(editor); } return editor; } } } IEditorInput input = getEditorInput(inputElement); if (input == null) { IStatus status = new Status(IStatus.ERROR, PHPUiPlugin.ID, IStatus.OK, "Can't open editor", //$NON-NLS-1$ null); throw new PartInitException(status); } return openInEditor(input, getEditorID(input), activate); } private static IEditorPart openInEditor(IEditorInput input, String editorID, boolean activate) throws PartInitException { IWorkbenchPage page = PHPUiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage(); if (page == null) { IStatus status = new Status(IStatus.ERROR, PHPUiPlugin.ID, IStatus.OK, "Can't open editor", //$NON-NLS-1$ null); throw new PartInitException(status); } return page.openEditor(input, editorID, activate); } private static IEditorInput getEditorInput(IModelElement element) { if (element != null) { IResource resource = element.getResource(); if (resource instanceof IFile) { return new FileEditorInput((IFile) resource); } } return null; } /** * Returns the editor id of give editor input. * * @param input * @return * @throws PartInitException */ public static String getEditorID(IEditorInput input) throws PartInitException { IEditorDescriptor editorDescriptor; if (input instanceof IFileEditorInput) { editorDescriptor = IDE.getEditorDescriptor(((IFileEditorInput) input).getFile(), true, false); } else { String name = input.getName(); if (name == null) { IStatus status = new Status(IStatus.ERROR, PHPUiPlugin.ID, IStatus.OK, "Can't find editor", null); //$NON-NLS-1$ throw new PartInitException(status); } editorDescriptor = IDE.getEditorDescriptor(name, true, false); } return editorDescriptor.getId(); } /** * Returns the current model element from the PHP editor * * @param event * @return * @throws ModelException */ public static IModelElement getCurrentModelElement(ExecutionEvent event) throws ExecutionException { PHPStructuredEditor textEditor = getPHPEditor(event); IModelElement modelElement = null; if (textEditor != null) { modelElement = textEditor.getModelElement(); } if (textEditor != null && modelElement != null) { final ISelectionProvider selectionProvider = textEditor.getSelectionProvider(); ISourceViewer viewer = textEditor.getTextViewer(); Point originalSelection = viewer.getSelectedRange(); int offset = -1; if (originalSelection == null) { final ISelection selection = selectionProvider.getSelection(); if (selection instanceof TextSelection) { TextSelection textSelection = (TextSelection) selection; offset = textSelection.getOffset(); } } else { offset = originalSelection.x; } if (modelElement instanceof ISourceModule) { ISourceModule module = (ISourceModule) modelElement; try { return module.getElementAt(offset); } catch (ModelException e) { throw new ExecutionException("Error trying to resolve document's element", e); //$NON-NLS-1$ } } } return null; } /** * Figure out the type parent of the given element * * @param element * @return the type which the element belongs to, or null if can't find. */ public static IType getType(IModelElement element) { if (element == null) { return null; } IModelElement model = element; while (!(model instanceof IType)) { if (model == null) { return null; } IModelElement parent = model.getParent(); if (parent == model) { return null; } model = parent; if (model instanceof ISourceModule) { return null; } } return (IType) model; } /** * Creates the Program instance for the given document. Especially the * Comment Mapper is initialised, so that the generating of the code can * aware of the existing of the comments. * * @param source * @param document * @param project * @return program instance. */ public static Program getASTRoot(ISourceModule source, IDocument document, IProject project) { ASTParser parserForExpected = ASTParser.newParser(ProjectOptions.getPHPVersion(project), source); Program program = null; try { parserForExpected.setSource(document.get().toCharArray()); program = parserForExpected.createAST(new NullProgressMonitor()); program.setSourceModule(source); final Reader reader = new StringReader(document.get()); program.initCommentMapper(document, new PHPAstLexer(reader)); program.recordModifications(); } catch (Exception e) { PHPUiPlugin.log(e); } return program; } private static class MethodDeclarationStub extends MethodDeclaration implements MethodStub { public MethodDeclarationStub(AST ast) { super(ast); } } }