| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed 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. |
| */ |
| |
| /* |
| * Class FieldBreakpoint |
| * @author Jeka |
| */ |
| package com.intellij.debugger.ui.breakpoints; |
| |
| import com.intellij.debugger.DebuggerBundle; |
| import com.intellij.debugger.DebuggerManagerEx; |
| import com.intellij.debugger.SourcePosition; |
| import com.intellij.debugger.engine.DebugProcessImpl; |
| import com.intellij.debugger.engine.SuspendContextImpl; |
| import com.intellij.debugger.engine.evaluation.EvaluateException; |
| import com.intellij.debugger.engine.jdi.VirtualMachineProxy; |
| import com.intellij.debugger.engine.requests.RequestManagerImpl; |
| import com.intellij.debugger.impl.PositionUtil; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.InvalidDataException; |
| import com.intellij.openapi.util.JDOMExternalizerUtil; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.Processor; |
| import com.intellij.util.text.CharArrayUtil; |
| import com.intellij.xdebugger.XDebuggerUtil; |
| import com.intellij.xdebugger.breakpoints.XBreakpoint; |
| import com.sun.jdi.*; |
| import com.sun.jdi.event.AccessWatchpointEvent; |
| import com.sun.jdi.event.LocatableEvent; |
| import com.sun.jdi.event.ModificationWatchpointEvent; |
| import com.sun.jdi.request.AccessWatchpointRequest; |
| import com.sun.jdi.request.ModificationWatchpointRequest; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties; |
| |
| import javax.swing.*; |
| |
| public class FieldBreakpoint extends BreakpointWithHighlighter<JavaFieldBreakpointProperties> { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.FieldBreakpoint"); |
| private boolean myIsStatic; |
| |
| @NonNls public static final Key<FieldBreakpoint> CATEGORY = BreakpointCategory.lookup("field_breakpoints"); |
| |
| protected FieldBreakpoint(Project project, XBreakpoint breakpoint) { |
| super(project, breakpoint); |
| } |
| |
| private FieldBreakpoint(Project project, @NotNull String fieldName, XBreakpoint breakpoint) { |
| super(project, breakpoint); |
| setFieldName(fieldName); |
| } |
| |
| public boolean isStatic() { |
| return myIsStatic; |
| } |
| |
| public String getFieldName() { |
| return getProperties().myFieldName; |
| } |
| |
| @Override |
| protected Icon getDisabledIcon(boolean isMuted) { |
| final Breakpoint master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this); |
| if (isMuted) { |
| return master == null? AllIcons.Debugger.Db_muted_disabled_field_breakpoint : AllIcons.Debugger.Db_muted_dep_field_breakpoint; |
| } |
| else { |
| return master == null? AllIcons.Debugger.Db_disabled_field_breakpoint : AllIcons.Debugger.Db_dep_field_breakpoint; |
| } |
| } |
| |
| @Override |
| protected Icon getSetIcon(boolean isMuted) { |
| return isMuted? AllIcons.Debugger.Db_muted_field_breakpoint : AllIcons.Debugger.Db_field_breakpoint; |
| } |
| |
| @Override |
| protected Icon getInvalidIcon(boolean isMuted) { |
| return isMuted? AllIcons.Debugger.Db_muted_invalid_field_breakpoint : AllIcons.Debugger.Db_invalid_field_breakpoint; |
| } |
| |
| @Override |
| protected Icon getVerifiedIcon(boolean isMuted) { |
| return isMuted? AllIcons.Debugger.Db_muted_verified_field_breakpoint : AllIcons.Debugger.Db_verified_field_breakpoint; |
| } |
| |
| @Override |
| protected Icon getVerifiedWarningsIcon(boolean isMuted) { |
| return isMuted? AllIcons.Debugger.Db_muted_field_warning_breakpoint : AllIcons.Debugger.Db_field_warning_breakpoint; |
| } |
| |
| @Override |
| public Key<FieldBreakpoint> getCategory() { |
| return CATEGORY; |
| } |
| |
| public PsiField getPsiField() { |
| final SourcePosition sourcePosition = getSourcePosition(); |
| final PsiField field = ApplicationManager.getApplication().runReadAction(new Computable<PsiField>() { |
| @Override |
| public PsiField compute() { |
| final PsiClass psiClass = getPsiClassAt(sourcePosition); |
| return psiClass != null ? psiClass.findFieldByName(getFieldName(), true) : null; |
| } |
| }); |
| if (field != null) { |
| return field; |
| } |
| return PositionUtil.getPsiElementAt(getProject(), PsiField.class, sourcePosition); |
| } |
| |
| @Override |
| protected void reload(PsiFile psiFile) { |
| super.reload(psiFile); |
| PsiField field = PositionUtil.getPsiElementAt(getProject(), PsiField.class, getSourcePosition()); |
| if(field != null) { |
| setFieldName(field.getName()); |
| PsiClass psiClass = field.getContainingClass(); |
| if (psiClass != null) { |
| getProperties().myClassName = psiClass.getQualifiedName(); |
| } |
| myIsStatic = field.hasModifierProperty(PsiModifier.STATIC); |
| } |
| if (myIsStatic) { |
| setInstanceFiltersEnabled(false); |
| } |
| } |
| |
| //@Override |
| //public boolean moveTo(@NotNull SourcePosition position) { |
| // final PsiField field = PositionUtil.getPsiElementAt(getProject(), PsiField.class, position); |
| // return field != null && super.moveTo(SourcePosition.createFromElement(field)); |
| //} |
| |
| @Override |
| protected ObjectReference getThisObject(SuspendContextImpl context, LocatableEvent event) throws EvaluateException { |
| if (event instanceof ModificationWatchpointEvent) { |
| ModificationWatchpointEvent modificationEvent = (ModificationWatchpointEvent)event; |
| ObjectReference reference = modificationEvent.object(); |
| if (reference != null) { // non-static |
| return reference; |
| } |
| } |
| else if (event instanceof AccessWatchpointEvent) { |
| AccessWatchpointEvent accessEvent = (AccessWatchpointEvent)event; |
| ObjectReference reference = accessEvent.object(); |
| if (reference != null) { // non-static |
| return reference; |
| } |
| } |
| |
| return super.getThisObject(context, event); |
| } |
| |
| @Override |
| public void createRequestForPreparedClass(DebugProcessImpl debugProcess, |
| ReferenceType refType) { |
| VirtualMachineProxy vm = debugProcess.getVirtualMachineProxy(); |
| try { |
| Field field = refType.fieldByName(getFieldName()); |
| if (field == null) { |
| debugProcess.getRequestsManager().setInvalid(this, DebuggerBundle.message("error.invalid.breakpoint.missing.field.in.class", |
| getFieldName(), refType.name())); |
| return; |
| } |
| RequestManagerImpl manager = debugProcess.getRequestsManager(); |
| if (isWatchModification() && vm.canWatchFieldModification()) { |
| ModificationWatchpointRequest request = manager.createModificationWatchpointRequest(this, field); |
| debugProcess.getRequestsManager().enableRequest(request); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Modification request added"); |
| } |
| } |
| if (isWatchAccess() && vm.canWatchFieldAccess()) { |
| AccessWatchpointRequest request = manager.createAccessWatchpointRequest(this, field); |
| debugProcess.getRequestsManager().enableRequest(request); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Access request added field = "+field.name() + "; refType = "+refType.name()); |
| } |
| } |
| } |
| catch (ObjectCollectedException ex) { |
| LOG.debug(ex); |
| } |
| catch (Exception ex) { |
| LOG.debug(ex); |
| } |
| } |
| |
| @Override |
| public String getEventMessage(final LocatableEvent event) { |
| final Location location = event.location(); |
| final String locationQName = location.declaringType().name() + "." + location.method().name(); |
| String locationFileName; |
| try { |
| locationFileName = location.sourceName(); |
| } |
| catch (AbsentInformationException e) { |
| locationFileName = getFileName(); |
| } |
| final int locationLine = location.lineNumber(); |
| |
| if (event instanceof ModificationWatchpointEvent) { |
| final ModificationWatchpointEvent modificationEvent = (ModificationWatchpointEvent)event; |
| final ObjectReference object = modificationEvent.object(); |
| final Field field = modificationEvent.field(); |
| if (object != null) { |
| return DebuggerBundle.message( |
| "status.field.watchpoint.reached.modification", |
| field.declaringType().name(), |
| field.name(), |
| modificationEvent.valueCurrent(), |
| modificationEvent.valueToBe(), |
| locationQName, |
| locationFileName, |
| locationLine, |
| object.uniqueID() |
| ); |
| } |
| return DebuggerBundle.message( |
| "status.static.field.watchpoint.reached.modification", |
| field.declaringType().name(), |
| field.name(), |
| modificationEvent.valueCurrent(), |
| modificationEvent.valueToBe(), |
| locationQName, |
| locationFileName, |
| locationLine |
| ); |
| } |
| if (event instanceof AccessWatchpointEvent) { |
| AccessWatchpointEvent accessEvent = (AccessWatchpointEvent)event; |
| final ObjectReference object = accessEvent.object(); |
| final Field field = accessEvent.field(); |
| if (object != null) { |
| return DebuggerBundle.message( |
| "status.field.watchpoint.reached.access", |
| field.declaringType().name(), |
| field.name(), |
| locationQName, |
| locationFileName, |
| locationLine, |
| object.uniqueID() |
| ); |
| } |
| return DebuggerBundle.message( |
| "status.static.field.watchpoint.reached.access", |
| field.declaringType().name(), |
| field.name(), |
| locationQName, |
| locationFileName, |
| locationLine |
| ); |
| } |
| return null; |
| } |
| |
| @Override |
| public String getDisplayName() { |
| if(!isValid()) { |
| return DebuggerBundle.message("status.breakpoint.invalid"); |
| } |
| final String className = getClassName(); |
| return className != null && !className.isEmpty() ? className + "." + getFieldName() : getFieldName(); |
| } |
| |
| public static FieldBreakpoint create(@NotNull Project project, String fieldName, XBreakpoint xBreakpoint) { |
| FieldBreakpoint breakpoint = new FieldBreakpoint(project, fieldName, xBreakpoint); |
| return (FieldBreakpoint)breakpoint.init(); |
| } |
| |
| //@Override |
| //public boolean canMoveTo(final SourcePosition position) { |
| // return super.canMoveTo(position) && PositionUtil.getPsiElementAt(getProject(), PsiField.class, position) != null; |
| //} |
| |
| @Override |
| public boolean isValid() { |
| return super.isValid() && getPsiField() != null; |
| } |
| |
| @Override |
| public boolean isAt(@NotNull Document document, int offset) { |
| PsiField field = findField(myProject, document, offset); |
| return field == getPsiField(); |
| } |
| |
| //protected static FieldBreakpoint create(@NotNull Project project, @NotNull Field field, ObjectReference object, XBreakpoint xBreakpoint) { |
| // String fieldName = field.name(); |
| // int line = 0; |
| // Document document = null; |
| // try { |
| // List locations = field.declaringType().allLineLocations(); |
| // if(!locations.isEmpty()) { |
| // Location location = (Location)locations.get(0); |
| // line = location.lineNumber(); |
| // VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(location.sourcePath()); |
| // if(file != null) { |
| // PsiFile psiFile = PsiManager.getInstance(project).findFile(file); |
| // if(psiFile != null) { |
| // document = PsiDocumentManager.getInstance(project).getDocument(psiFile); |
| // } |
| // } |
| // } |
| // } |
| // catch (AbsentInformationException e) { |
| // LOG.debug(e); |
| // } |
| // catch (InternalError e) { |
| // LOG.debug(e); |
| // } |
| // |
| // if(document == null) return null; |
| // |
| // FieldBreakpoint fieldBreakpoint = new FieldBreakpoint(project, createHighlighter(project, document, line), fieldName, xBreakpoint); |
| // if (!fieldBreakpoint.isStatic()) { |
| // fieldBreakpoint.addInstanceFilter(object.uniqueID()); |
| // } |
| // return (FieldBreakpoint)fieldBreakpoint.init(); |
| //} |
| |
| public static PsiField findField(Project project, Document document, int offset) { |
| PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document); |
| if(file == null) return null; |
| offset = CharArrayUtil.shiftForward(document.getCharsSequence(), offset, " \t"); |
| PsiElement element = file.findElementAt(offset); |
| if(element == null) return null; |
| PsiField field = PsiTreeUtil.getParentOfType(element, PsiField.class, false); |
| int line = document.getLineNumber(offset); |
| if(field == null) { |
| final PsiField[] fld = {null}; |
| XDebuggerUtil.getInstance().iterateLine(project, document, line, new Processor<PsiElement>() { |
| @Override |
| public boolean process(PsiElement element) { |
| PsiField field = PsiTreeUtil.getParentOfType(element, PsiField.class, false); |
| if(field != null) { |
| fld[0] = field; |
| return false; |
| } |
| return true; |
| } |
| }); |
| field = fld[0]; |
| } |
| |
| return field; |
| } |
| |
| @Override |
| public void readExternal(@NotNull Element breakpointNode) throws InvalidDataException { |
| super.readExternal(breakpointNode); |
| //noinspection HardCodedStringLiteral |
| setFieldName(breakpointNode.getAttributeValue("field_name")); |
| if(getFieldName() == null) { |
| throw new InvalidDataException("No field name for field breakpoint"); |
| } |
| try { |
| getProperties().WATCH_MODIFICATION = Boolean.valueOf(JDOMExternalizerUtil.readField(breakpointNode, "WATCH_MODIFICATION")); |
| } catch (Exception e) { |
| } |
| try { |
| getProperties().WATCH_ACCESS = Boolean.valueOf(JDOMExternalizerUtil.readField(breakpointNode, "WATCH_ACCESS")); |
| } catch (Exception e) { |
| } |
| } |
| // |
| //@Override |
| //@SuppressWarnings({"HardCodedStringLiteral"}) |
| //public void writeExternal(@NotNull Element parentNode) throws WriteExternalException { |
| // super.writeExternal(parentNode); |
| // parentNode.setAttribute("field_name", getFieldName()); |
| //} |
| |
| @Override |
| public PsiElement getEvaluationElement() { |
| return getPsiClass(); |
| } |
| |
| private boolean isWatchModification() { |
| return getProperties().WATCH_MODIFICATION; |
| } |
| |
| private boolean isWatchAccess() { |
| return getProperties().WATCH_ACCESS; |
| } |
| |
| void setFieldName(String fieldName) { |
| getProperties().myFieldName = fieldName; |
| } |
| } |