/******************************************************************************* * Copyright (c) 2009, 2015 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.internal.debug.ui.hovers; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.dltk.core.IField; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.internal.ui.text.hover.AbstractScriptEditorTextHover; import org.eclipse.jface.text.*; 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.compiler.ast.nodes.NamespaceReference; import org.eclipse.php.internal.core.corext.dom.NodeFinder; import org.eclipse.php.internal.debug.core.PHPDebugPlugin; import org.eclipse.php.internal.debug.core.zend.debugger.Expression; import org.eclipse.php.internal.debug.core.zend.debugger.ExpressionsUtil; import org.eclipse.php.internal.debug.core.zend.model.PHPDebugTarget; import org.eclipse.php.internal.debug.core.zend.model.PHPStackFrame; import org.eclipse.php.internal.debug.core.zend.model.PHPVariable; import org.eclipse.php.ui.editor.SharedASTProvider; import org.eclipse.php.ui.editor.hover.IHoverMessageDecorator; import org.eclipse.php.ui.editor.hover.IPHPTextHover; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.part.FileEditorInput; public class PHPDebugTextHover extends AbstractScriptEditorTextHover implements IPHPTextHover, ITextHoverExtension2 { private ExpressionsUtil expressionsUtil; public PHPDebugTextHover() { } public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { Object hoverInfo = getHoverInfo2(textViewer, hoverRegion); return hoverInfo == null ? null : hoverInfo.toString(); } public IHoverMessageDecorator getMessageDecorator() { return null; } /** * Returns the stack frame in which to search for variables, or * <code>null</code> if none. * * @return the stack frame in which to search for variables, or * <code>null</code> if none */ protected IStackFrame getFrame() { IAdaptable adaptable = DebugUITools.getDebugContext(); if (adaptable != null) { return adaptable.getAdapter(PHPStackFrame.class); } return null; } public IInformationControlCreator getHoverControlCreator() { return new ExpressionInformationControlCreator(); } // Returns the php debug target that is in contex. // In case that protected PHPDebugTarget getDebugTarget() { IAdaptable adaptable = DebugUITools.getDebugContext(); if (adaptable instanceof PHPStackFrame) { PHPStackFrame stackFrame = (PHPStackFrame) adaptable; IEditorInput ei = getEditor().getEditorInput(); if (ei instanceof FileEditorInput) { FileEditorInput fi = (FileEditorInput) ei; // Check for the file path within the project String fileInDebug = stackFrame.getSourceName(); String fileInProject = fi.getFile().getProjectRelativePath().toString(); if (fileInDebug != null && (fileInDebug.endsWith('/' + fileInProject) || fileInDebug.equals(fileInProject))) { PHPDebugTarget debugTarget = (PHPDebugTarget) stackFrame.getDebugTarget(); return debugTarget; } } else { // File on the include Path PHPDebugTarget debugTarget = (PHPDebugTarget) stackFrame.getDebugTarget(); return debugTarget; } } return null; } @Override public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { PHPStackFrame frame = (PHPStackFrame) getFrame(); if (frame == null) return null; expressionsUtil = ExpressionsUtil.getInstance(getDebugTarget().getExpressionManager()); IVariable variable = null; try { ISourceModule sourceModule = getEditorInputModelElement(); ASTNode root = SharedASTProvider.getAST(sourceModule, SharedASTProvider.WAIT_NO, null); if (root == null) { ASTParser parser = ASTParser.newParser(sourceModule); root = parser.createAST(null); } ASTNode node = NodeFinder.perform(root, hoverRegion.getOffset(), hoverRegion.getLength()); if (node instanceof Scalar) { Scalar scalar = (Scalar) node; if (node.getParent() instanceof ArrayAccess) { ArrayAccess access = (ArrayAccess) node.getParent(); Expression expression = expressionsUtil.buildExpression(computeExpression(access.getName())); Expression[] children = expression.getValue().getOriChildren(); if (children != null && children.length > 0) { for (Expression child : children) { String name = child.getLastName(); if (scalar.getScalarType() == Scalar.TYPE_STRING) { name = "\"" + name + "\""; //$NON-NLS-1$ //$NON-NLS-2$ } if (name.equals(scalar.getStringValue())) { variable = new PHPVariable(getDebugTarget(), child); } } } } else if (!(scalar.getParent() instanceof Include) && scalar.getScalarType() == Scalar.TYPE_STRING) { if (!(scalar.getStringValue().startsWith("\"") && scalar.getStringValue().endsWith("\""))) { //$NON-NLS-1$ //$NON-NLS-2$ if (!scalar.getStringValue().trim().equals("")) { Expression constant = expressionsUtil.fetchConstant(scalar.getStringValue()); variable = new PHPVariable(getDebugTarget(), constant); } } } } else if (node.getParent() instanceof Variable && node.getParent().getParent() instanceof FieldAccess) { String nodeName = ((Identifier) node).getName(); String expression = computeExpression(((FieldAccess) node.getParent().getParent()).getDispatcher()); variable = fetchClassMember(expression, nodeName); } else if (node.getParent() instanceof StaticConstantAccess) { String nodeName = ((Identifier) node).getName(); StaticConstantAccess staticAccess = (StaticConstantAccess) node.getParent(); String className = resolveTypeName((Identifier) staticAccess.getClassName()); if (className != null) { if (nodeName.equals("class")) { //$NON-NLS-1$ variable = new PHPVariable(getDebugTarget(), expressionsUtil.fetchClassContext(className)); } else { variable = fetchClassConstant(className, nodeName); } } } else if (node.getParent() instanceof StaticFieldAccess) { Variable var = (Variable) node; String nodeName = ((Identifier) var.getName()).getName(); StaticFieldAccess staticAccess = (StaticFieldAccess) node.getParent(); Identifier identifier = null; if (staticAccess.getClassName() instanceof Identifier) { identifier = (Identifier) staticAccess.getClassName(); } else if (staticAccess.getClassName() instanceof VariableBase) { identifier = (Identifier) var.getName(); } variable = fetchStaticMember(identifier, nodeName); } else if (node.getParent() instanceof ConstantDeclaration) { String nodeName = ((Identifier) node).getName(); IField field = (IField) sourceModule.getElementAt(node.getStart()); if (field.getParent() instanceof IType) { IType type = (IType) field.getParent(); String typeName = type.getFullyQualifiedName(NamespaceReference.NAMESPACE_DELIMITER); if (!PHPFlags.isNamespace(type.getFlags())) { variable = fetchClassConstant(typeName, nodeName); } else { Expression constant = expressionsUtil .fetchConstant(typeName + NamespaceReference.NAMESPACE_DELIMITER + nodeName); variable = new PHPVariable(getDebugTarget(), constant); } } } else if (node.getParent() instanceof SingleFieldDeclaration) { IField field = (IField) sourceModule.getElementAt(node.getStart()); String typeName = ""; boolean isAnonymous = false; if (field.getParent() instanceof IType) { IType type = (IType) field.getParent(); typeName = type.getFullyQualifiedName(NamespaceReference.NAMESPACE_DELIMITER); isAnonymous = PHPFlags.isAnonymous(type.getFlags()); } Variable var = (Variable) node; String nodeName = ((Identifier) var.getName()).getName(); if (!PHPFlags.isStatic(field.getFlags())) { Expression e = expressionsUtil.buildExpression("$this"); //$NON-NLS-1$ if (isAnonymous || typeName.equals(e.getValue().getValue().toString())) { variable = fetchClassMember(e, nodeName); } } else { variable = fetchStaticMember(typeName, nodeName); } } else { // local variables String variableName = null; // ${a} if (node instanceof Identifier && node.getParent() instanceof Variable && !((Variable) node.getParent()).isDollared()) { variableName = "$" + ((Identifier) node).getName(); //$NON-NLS-1$ } else { IDocument document = textViewer.getDocument(); if (document != null) { // $$a if (node instanceof ReflectionVariable) { variableName = document.get(((ReflectionVariable) node).getName().getStart(), ((ReflectionVariable) node).getName().getLength()); } else { // $a variableName = document.get(hoverRegion.getOffset(), hoverRegion.getLength()); } } } variable = frame.findVariable(variableName); } } catch (Exception e) { PHPDebugPlugin.log(e); } return variable; } protected String computeExpression(VariableBase node) { final StringBuffer dispatcher = new StringBuffer(); node.accept(new AbstractVisitor() { private boolean isFirstVariable = true; public boolean visit(Identifier identifier) { if (identifier.getParent() instanceof Identifier) { String typeName = resolveTypeName((Identifier) identifier.getParent()); if (typeName != null) dispatcher.append(typeName); } else if (identifier.getParent() instanceof Variable) { Variable variable = (Variable) identifier.getParent(); if (variable.isDollared()) { if (!isFirstVariable) { dispatcher.append("::"); //$NON-NLS-1$ } dispatcher.append("$"); //$NON-NLS-1$ } else { if (!isFirstVariable) { dispatcher.append("->"); //$NON-NLS-1$ } } dispatcher.append(((Identifier) variable.getName()).getName()); } isFirstVariable = false; return false; }; }); return dispatcher.toString(); } private PHPVariable fetchClassMember(String expression, String fieldName) { return fetchClassMember(expressionsUtil.buildExpression(expression), fieldName); } private PHPVariable fetchClassMember(Expression expression, String fieldName) { if (expression.getValue().getOriChildren() == null) return null; for (Expression child : expression.getValue().getOriChildren()) { if (child.getLastName().endsWith(fieldName)) { return new PHPVariable(getDebugTarget(), child); } } return null; } private PHPVariable fetchStaticMember(String className, String fieldName) { Expression[] staticMembers = expressionsUtil.fetchStaticMembers(className); for (Expression child : staticMembers) { if (child.getLastName().endsWith(fieldName)) { return new PHPVariable(getDebugTarget(), child); } } return null; } private PHPVariable fetchStaticMember(Identifier type, String fieldName) { String className = resolveTypeName(type); if (className != null) { return fetchStaticMember(className, fieldName); } return null; } private PHPVariable fetchClassConstant(String className, String constantName) { Expression[] constants = expressionsUtil.fetchClassConstants(className); for (Expression child : constants) { if (child.getLastName().equals(constantName)) { return new PHPVariable(getDebugTarget(), child); } } return null; } protected String resolveTypeName(Identifier type) { ITypeBinding typeBinding = type.resolveTypeBinding(); String className = null; if (typeBinding != null) { className = typeBinding.getName(); if (className.startsWith(NamespaceReference.NAMESPACE_DELIMITER)) { className = className.substring(1); } } else { className = type.getName(); } return className; } }