Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425
from branch master of git://git.jetbrains.org/idea/community.git
diff --git a/java/debugger/impl/debugger-impl.iml b/java/debugger/impl/debugger-impl.iml
new file mode 100644
index 0000000..8b9d265
--- /dev/null
+++ b/java/debugger/impl/debugger-impl.iml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="debugger-openapi" exported="" />
+ <orderEntry type="module" module-name="openapi" />
+ <orderEntry type="module" module-name="execution-openapi" />
+ <orderEntry type="module" module-name="resources" />
+ <orderEntry type="module" module-name="xdebugger-api" />
+ <orderEntry type="module" module-name="xdebugger-impl" />
+ <orderEntry type="module" module-name="lang-api" />
+ <orderEntry type="module" module-name="compiler-openapi" />
+ <orderEntry type="module" module-name="java-runtime" />
+ <orderEntry type="module" module-name="jsp-openapi" />
+ <orderEntry type="module" module-name="java-impl" />
+ </component>
+ <component name="copyright">
+ <Base>
+ <setting name="state" value="1" />
+ </Base>
+ </component>
+</module>
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/DebugEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DebugEnvironment.java
new file mode 100644
index 0000000..af8662b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DebugEnvironment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: michael.golubev
+ */
+public interface DebugEnvironment {
+
+ @Nullable
+ ExecutionResult createExecutionResult() throws ExecutionException;
+
+ GlobalSearchScope getSearchScope();
+
+ boolean isRemote();
+
+ RemoteConnection getRemoteConnection();
+
+ boolean isPollConnection();
+
+ String getSessionName();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/DebugException.java b/java/debugger/impl/src/com/intellij/debugger/DebugException.java
new file mode 100644
index 0000000..c92b9b5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DebugException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger;
+
+public class DebugException extends RuntimeException {
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public DebugException() {
+ super("DebugException");
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java
new file mode 100644
index 0000000..8c89bad
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.diagnostic.logging.LogFilesManager;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: michael.golubev
+ */
+public interface DebugUIEnvironment {
+
+ DebugEnvironment getEnvironment();
+
+ @Nullable
+ RunContentDescriptor getReuseContent();
+
+ @Nullable
+ Icon getIcon();
+
+ void initLogs(RunContentDescriptor content, LogFilesManager logFilesManager);
+
+ void initActions(RunContentDescriptor content, DefaultActionGroup actionGroup);
+
+ @Nullable
+ RunProfile getRunProfile();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/DebuggerInvocationUtil.java b/java/debugger/impl/src/com/intellij/debugger/DebuggerInvocationUtil.java
new file mode 100644
index 0000000..c0dfbb9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DebuggerInvocationUtil.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiDocumentManager;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+public class DebuggerInvocationUtil {
+ public static void swingInvokeLater(final Project project, @NotNull final Runnable runnable) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ if (project != null && !project.isDisposed()) {
+ runnable.run();
+ }
+ }
+ });
+ }
+ public static void invokeLater(final Project project, @NotNull final Runnable runnable) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ if (project != null && !project.isDisposed()) {
+ runnable.run();
+ }
+ }
+ });
+ }
+
+ public static void invokeLater(final Project project, @NotNull final Runnable runnable, ModalityState state) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ if(project == null || project.isDisposed()) return;
+
+ runnable.run();
+ }
+ }, state);
+ }
+
+ public static void invokeAndWait(final Project project, @NotNull final Runnable runnable, ModalityState state) {
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ public void run() {
+ if(project == null || project.isDisposed()) return;
+
+ runnable.run();
+ }
+ }, state);
+ }
+
+ public static <T> T commitAndRunReadAction(Project project, final EvaluatingComputable<T> computable) throws EvaluateException {
+ final Throwable[] ex = new Throwable[] { null };
+ T result = PsiDocumentManager.getInstance(project).commitAndRunReadAction(new Computable<T>() {
+ public T compute() {
+ try {
+ return computable.compute();
+ }
+ catch (RuntimeException e) {
+ ex[0] = e;
+ }
+ catch (Exception th) {
+ ex[0] = th;
+ }
+
+ return null;
+ }
+ });
+
+ if(ex[0] != null) {
+ if(ex[0] instanceof RuntimeException) {
+ throw (RuntimeException)ex[0];
+ }
+ else {
+ throw (EvaluateException) ex[0];
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/DebuggerManagerEx.java b/java/debugger/impl/src/com/intellij/debugger/DebuggerManagerEx.java
new file mode 100644
index 0000000..0384e6e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DebuggerManagerEx.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerManagerListener;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.ModuleRunProfile;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.project.Project;
+
+import java.util.Collection;
+
+public abstract class DebuggerManagerEx extends DebuggerManager {
+ public static DebuggerManagerEx getInstanceEx(Project project) {
+ return (DebuggerManagerEx)DebuggerManager.getInstance(project);
+ }
+ public abstract BreakpointManager getBreakpointManager();
+
+ public abstract Collection<DebuggerSession> getSessions();
+ public abstract DebuggerSession getSession(DebugProcess debugProcess);
+
+ public abstract DebuggerContextImpl getContext();
+ public abstract DebuggerStateManager getContextManager();
+
+ public abstract void addDebuggerManagerListener(DebuggerManagerListener debuggerManagerListener);
+ public abstract void removeDebuggerManagerListener(DebuggerManagerListener debuggerManagerListener);
+
+ public abstract DebuggerSession attachVirtualMachine(Executor executor,
+ ProgramRunner runner,
+ ModuleRunProfile profile,
+ RunProfileState state,
+ RemoteConnection connection,
+ boolean pollConnection
+ ) throws ExecutionException;
+
+ public abstract DebuggerSession attachVirtualMachine(DebugEnvironment environment) throws ExecutionException;
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/DefaultDebugEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugEnvironment.java
new file mode 100644
index 0000000..dc2e1af
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugEnvironment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.*;
+import com.intellij.execution.filters.ExceptionFilters;
+import com.intellij.execution.filters.Filter;
+import com.intellij.execution.filters.TextConsoleBuilder;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: michael.golubev
+ */
+public class DefaultDebugEnvironment implements DebugEnvironment {
+
+ private final GlobalSearchScope mySearchScope;
+ private final Executor myExecutor;
+ private final ProgramRunner myRunner;
+ private RunProfileState myState;
+ private final RemoteConnection myRemoteConnection;
+ private final boolean myPollConnection;
+ private final RunProfile myRunProfile;
+
+ public DefaultDebugEnvironment(Project project,
+ Executor executor,
+ ProgramRunner runner,
+ RunProfile runProfile,
+ RunProfileState state,
+ RemoteConnection remoteConnection,
+ boolean pollConnection) {
+ myExecutor = executor;
+ myRunner = runner;
+ myRunProfile = runProfile;
+ myState = state;
+ myRemoteConnection = remoteConnection;
+ myPollConnection = pollConnection;
+
+ mySearchScope = createSearchScope(project, runProfile);
+ }
+
+ @NotNull
+ public static GlobalSearchScope createSearchScope(Project project, RunProfile runProfile) {
+ Module[] modules = null;
+ if (runProfile instanceof ModuleRunProfile) {
+ modules = ((ModuleRunProfile)runProfile).getModules();
+ }
+ if (modules == null || modules.length == 0) {
+ return GlobalSearchScope.allScope(project);
+ }
+ else {
+ GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(modules[0], true);
+ for (int idx = 1; idx < modules.length; idx++) {
+ Module module = modules[idx];
+ scope = scope.uniteWith(GlobalSearchScope.moduleRuntimeScope(module, true));
+ }
+ return scope;
+ }
+ }
+
+ @Override
+ public ExecutionResult createExecutionResult() throws ExecutionException {
+ if (myState instanceof CommandLineState) {
+ final TextConsoleBuilder consoleBuilder = ((CommandLineState)myState).getConsoleBuilder();
+ if (consoleBuilder != null) {
+ List<Filter> filters = ExceptionFilters.getFilters(mySearchScope);
+ for (Filter filter : filters) {
+ consoleBuilder.addFilter(filter);
+ }
+ }
+ }
+ return myState.execute(myExecutor, myRunner);
+ }
+
+ @Override
+ public GlobalSearchScope getSearchScope() {
+ return mySearchScope;
+ }
+
+ @Override
+ public boolean isRemote() {
+ return myState instanceof RemoteState;
+ }
+
+ @Override
+ public RemoteConnection getRemoteConnection() {
+ return myRemoteConnection;
+ }
+
+ @Override
+ public boolean isPollConnection() {
+ return myPollConnection;
+ }
+
+ @Override
+ public String getSessionName() {
+ return myRunProfile.getName();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java
new file mode 100644
index 0000000..cacbf8e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.diagnostic.logging.LogFilesManager;
+import com.intellij.diagnostic.logging.OutputFileUtil;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunConfigurationBase;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.runners.RestartAction;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.actions.CloseAction;
+import com.intellij.ide.actions.ContextHelpAction;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.Constraints;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: michael.golubev
+ */
+public class DefaultDebugUIEnvironment implements DebugUIEnvironment {
+
+ private final Project myProject;
+ private final Executor myExecutor;
+ private final ProgramRunner myRunner;
+ private final ExecutionEnvironment myExecutionEnvironment;
+ @Nullable private RunContentDescriptor myReuseContent;
+ private final RunProfile myRunProfile;
+ private final DebugEnvironment myModelEnvironment;
+
+ public DefaultDebugUIEnvironment(Project project,
+ Executor executor,
+ ProgramRunner runner,
+ ExecutionEnvironment environment,
+ RunProfileState state,
+ @Nullable RunContentDescriptor reuseContent,
+ RemoteConnection remoteConnection,
+ boolean pollConnection) {
+ myProject = project;
+ myExecutor = executor;
+ myRunner = runner;
+ myExecutionEnvironment = environment;
+ myRunProfile = environment.getRunProfile();
+ myModelEnvironment = new DefaultDebugEnvironment(project,
+ executor,
+ runner,
+ myRunProfile,
+ state,
+ remoteConnection,
+ pollConnection);
+ myReuseContent = reuseContent;
+ if (myReuseContent != null) {
+ Disposer.register(myReuseContent, new Disposable() {
+ @Override
+ public void dispose() {
+ myReuseContent = null;
+ }
+ });
+ }
+ }
+
+ @Override
+ public DebugEnvironment getEnvironment() {
+ return myModelEnvironment;
+ }
+
+ @Nullable
+ @Override
+ public RunContentDescriptor getReuseContent() {
+ return myReuseContent;
+ }
+
+ @Override
+ public Icon getIcon() {
+ return myRunProfile.getIcon();
+ }
+
+ @Override
+ public void initLogs(RunContentDescriptor content, LogFilesManager logFilesManager) {
+ ProcessHandler processHandler = content.getProcessHandler();
+ if (myRunProfile instanceof RunConfigurationBase) {
+ RunConfigurationBase runConfiguration = (RunConfigurationBase)myRunProfile;
+
+ logFilesManager.registerFileMatcher(runConfiguration);
+
+ logFilesManager.initLogConsoles(runConfiguration, processHandler);
+ OutputFileUtil.attachDumpListener(runConfiguration, processHandler, content.getExecutionConsole());
+ }
+ }
+
+ @Override
+ public void initActions(RunContentDescriptor content, DefaultActionGroup actionGroup) {
+ ProcessHandler processHandler = content.getProcessHandler();
+ RestartAction restartAction = new RestartAction(myExecutor,
+ myRunner,
+ processHandler,
+ RestartAction.RERUN_DEBUGGER_ICON,
+ content,
+ myExecutionEnvironment);
+ actionGroup.add(restartAction, Constraints.FIRST);
+ restartAction.registerShortcut(content.getComponent());
+
+ actionGroup.add(new CloseAction(myExecutor, content, myProject));
+ actionGroup.add(new ContextHelpAction(myExecutor.getHelpId()));
+ }
+
+ @Override
+ public RunProfile getRunProfile() {
+ return myRunProfile;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/EvaluatingComputable.java b/java/debugger/impl/src/com/intellij/debugger/EvaluatingComputable.java
new file mode 100644
index 0000000..1abe53b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/EvaluatingComputable.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+
+public interface EvaluatingComputable <T>{
+ T compute() throws EvaluateException;
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/HelpID.java b/java/debugger/impl/src/com/intellij/debugger/HelpID.java
new file mode 100644
index 0000000..0dd127d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/HelpID.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Nov 12, 2002
+ * Time: 3:25:10 PM
+ */
+package com.intellij.debugger;
+
+import org.jetbrains.annotations.NonNls;
+
+public interface HelpID {
+ @NonNls String LINE_BREAKPOINTS = "debugging.lineBreakpoint";
+ @NonNls String METHOD_BREAKPOINTS = "debugging.methodBreakpoint";
+ @NonNls String EXCEPTION_BREAKPOINTS = "debugging.exceptionBreakpoint";
+ @NonNls String FIELD_WATCHPOINTS = "debugging.fieldWatchpoint";
+ @NonNls String EVALUATE = "debugging.debugMenu.evaluate";
+ @NonNls String EXPORT_THREADS = "reference.run.export.thread";
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/InstanceFilter.java b/java/debugger/impl/src/com/intellij/debugger/InstanceFilter.java
new file mode 100644
index 0000000..df0ff53d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/InstanceFilter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.ui.classFilter.ClassFilter;
+import org.jdom.Element;
+
+/**
+ * User: lex
+ * Date: Aug 29, 2003
+ * Time: 2:49:27 PM
+ */
+public class InstanceFilter implements JDOMExternalizable{
+ public static final InstanceFilter[] EMPTY_ARRAY = new InstanceFilter[0];
+
+ public long ID = 0;
+ public boolean ENABLED = true;
+
+ protected InstanceFilter(long ID, boolean ENABLED) {
+ this.ID = ID;
+ this.ENABLED = ENABLED;
+ }
+
+ public long getId() {
+ return ID;
+ }
+
+ public boolean isEnabled() {
+ return ENABLED;
+ }
+
+ public void setId(long id) {
+ ID = id;
+ }
+
+ public void setEnabled(boolean enabled) {
+ ENABLED = enabled;
+ }
+
+ public static InstanceFilter create(String pattern) {
+ return new InstanceFilter(Long.parseLong(pattern), true);
+ }
+
+ public static InstanceFilter create(final ClassFilter filter) {
+ return new InstanceFilter(Long.parseLong(filter.getPattern()), filter.isEnabled());
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ DefaultJDOMExternalizer.readExternal(this, element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ }
+
+ public static ClassFilter[] createClassFilters(InstanceFilter[] filters) {
+ ClassFilter [] cFilters = new ClassFilter[filters.length];
+ for (int i = 0; i < cFilters.length; i++) {
+ InstanceFilter instanceFilter = filters[i];
+
+ ClassFilter classFilter = new ClassFilter();
+ classFilter.setEnabled(instanceFilter.isEnabled());
+ classFilter.setPattern(Long.toString(instanceFilter.getId()));
+
+ cFilters[i] = classFilter;
+ }
+ return cFilters;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AbstractSteppingActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/AbstractSteppingActionHandler.java
new file mode 100644
index 0000000..8fdcfb9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/AbstractSteppingActionHandler.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class AbstractSteppingActionHandler extends DebuggerActionHandler {
+
+ @Nullable
+ protected static DebuggerSession getSession(@NotNull Project project) {
+ final DebuggerContextImpl context = getContext(project);
+ return context != null ? context.getDebuggerSession() : null;
+ }
+
+ @Nullable
+ private static DebuggerContextImpl getContext(@NotNull Project project) {
+ return (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ }
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ final DebuggerContextImpl context = getContext(project);
+ if (context == null) {
+ return false;
+ }
+
+ DebuggerSession debuggerSession = context.getDebuggerSession();
+
+ final boolean isPaused = debuggerSession != null && debuggerSession.isPaused();
+ final SuspendContextImpl suspendContext = context.getSuspendContext();
+ final boolean hasCurrentThread = suspendContext != null && suspendContext.getThread() != null;
+ return isPaused && hasCurrentThread;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AddToWatchActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/AddToWatchActionHandler.java
new file mode 100644
index 0000000..8e95e99
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/AddToWatchActionHandler.java
@@ -0,0 +1,168 @@
+/*
+ * 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 AddToWatchActionHandler
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.debugger.ui.impl.MainWatchPanel;
+import com.intellij.debugger.ui.impl.VariablesPanel;
+import com.intellij.debugger.ui.impl.WatchDebuggerTree;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import com.intellij.xdebugger.impl.actions.XDebuggerActions;
+import org.jetbrains.annotations.NotNull;
+
+public class AddToWatchActionHandler extends DebuggerActionHandler {
+ @Override
+ public boolean isEnabled(@NotNull Project project, AnActionEvent event) {
+ DebuggerTreeNodeImpl[] selectedNodes = DebuggerAction.getSelectedNodes(event.getDataContext());
+ boolean enabled = false;
+ if (selectedNodes != null && selectedNodes.length > 0) {
+ if (DebuggerAction.getPanel(event.getDataContext()) instanceof VariablesPanel) {
+ enabled = true;
+ for (DebuggerTreeNodeImpl node : selectedNodes) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (!(descriptor instanceof ValueDescriptorImpl)) {
+ enabled = false;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ final Editor editor = event.getData(PlatformDataKeys.EDITOR);
+ enabled = DebuggerUtilsEx.getEditorText(editor) != null;
+ }
+ return enabled;
+ }
+
+ @Override
+ public void perform(@NotNull Project project, AnActionEvent event) {
+ final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(event.getDataContext());
+
+ if(debuggerContext == null) return;
+
+ final DebuggerSession session = debuggerContext.getDebuggerSession();
+ if(session == null) {
+ return;
+ }
+ final MainWatchPanel watchPanel = DebuggerPanelsManager.getInstance(debuggerContext.getProject()).getWatchPanel();
+
+ if(watchPanel == null) {
+ return;
+ }
+
+ final DebuggerTreeNodeImpl[] selectedNodes = DebuggerAction.getSelectedNodes(event.getDataContext());
+
+ if(selectedNodes != null && selectedNodes.length > 0) {
+ addFromNodes(debuggerContext, watchPanel, selectedNodes);
+ }
+ else {
+ final Editor editor = event.getData(PlatformDataKeys.EDITOR);
+ if (editor != null) {
+ final TextWithImports editorText = DebuggerUtilsEx.getEditorText(editor);
+ if (editorText != null) {
+ doAddWatch(watchPanel, editorText, null);
+ }
+ }
+ }
+ }
+
+ public static void addFromNodes(final DebuggerContextImpl debuggerContext, final MainWatchPanel watchPanel, final DebuggerTreeNodeImpl[] selectedNodes) {
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new AddToWatchesCommand(debuggerContext, selectedNodes, watchPanel));
+ }
+
+ public static void doAddWatch(final MainWatchPanel watchPanel, final TextWithImports expression, final NodeDescriptorImpl descriptor) {
+ final WatchDebuggerTree watchTree = watchPanel.getWatchTree();
+ final DebuggerTreeNodeImpl node = watchTree.addWatch(expression, null);
+ if (descriptor != null) {
+ node.getDescriptor().displayAs(descriptor);
+ }
+ node.calcValue();
+ }
+
+ private static class AddToWatchesCommand extends DebuggerContextCommandImpl {
+
+ private final DebuggerContextImpl myDebuggerContext;
+ private final DebuggerTreeNodeImpl[] mySelectedNodes;
+ private final MainWatchPanel myWatchPanel;
+
+ public AddToWatchesCommand(DebuggerContextImpl debuggerContext, DebuggerTreeNodeImpl[] selectedNodes, MainWatchPanel watchPanel) {
+ super(debuggerContext);
+ myDebuggerContext = debuggerContext;
+ mySelectedNodes = selectedNodes;
+ myWatchPanel = watchPanel;
+ }
+
+ public void threadAction() {
+ for (final DebuggerTreeNodeImpl node : mySelectedNodes) {
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ final Project project = myDebuggerContext.getDebuggerSession().getProject();
+ try {
+ final TextWithImports expression = DebuggerTreeNodeExpression.createEvaluationText(node, myDebuggerContext);
+ if (expression != null) {
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ doAddWatch(myWatchPanel, expression, descriptor);
+ }
+ });
+ }
+ }
+ catch (final EvaluateException e) {
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ Messages.showErrorDialog(project, e.getMessage(), ActionsBundle.actionText(XDebuggerActions.ADD_TO_WATCH));
+ }
+ });
+ }
+ }
+ }
+
+ protected void commandCancelled() {
+ DebuggerInvocationUtil.swingInvokeLater(myDebuggerContext.getProject(), new Runnable() {
+ public void run() {
+ for (DebuggerTreeNodeImpl node : mySelectedNodes) {
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (descriptor instanceof WatchItemDescriptor) {
+ final TextWithImports expression = ((WatchItemDescriptor)descriptor).getEvaluationText();
+ if (expression != null) {
+ doAddWatch(myWatchPanel, expression, descriptor);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java
new file mode 100644
index 0000000..bb74b8e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.settings.ArrayRendererConfigurable;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.render.*;
+import com.intellij.ide.actions.ShowSettingsUtilImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ex.SingleConfigurableEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.Nullable;
+
+public class AdjustArrayRangeAction extends DebuggerAction {
+ public void actionPerformed(AnActionEvent e) {
+ DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
+ if(debuggerContext == null) {
+ return;
+ }
+
+ DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+ if(debugProcess == null) {
+ return;
+ }
+
+ final Project project = debuggerContext.getProject();
+
+ final DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if (selectedNode == null) {
+ return;
+ }
+ NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ if(!(descriptor instanceof ValueDescriptorImpl /*&& ((ValueDescriptorImpl)descriptor).isArray()*/)) {
+ return;
+ }
+
+ final ArrayRenderer renderer = getArrayRenderer((ValueDescriptorImpl)descriptor)/*(ArrayRenderer)((ValueDescriptorImpl)selectedNode.getDescriptor()).getLastRenderer()*/;
+ if (renderer == null) {
+ return;
+ }
+
+ String title = createNodeTitle("", selectedNode);
+ String label = selectedNode.toString();
+ int index = label.indexOf('=');
+ if (index > 0) {
+ title = title + " " + label.substring(index);
+ }
+ final ArrayRenderer clonedRenderer = renderer.clone();
+ final NamedArrayConfigurable configurable = new NamedArrayConfigurable(title, clonedRenderer);
+ SingleConfigurableEditor editor = new SingleConfigurableEditor(project, configurable,
+ ShowSettingsUtilImpl.createDimensionKey(configurable), false);
+ editor.show();
+
+ if(editor.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
+ debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
+ public void contextAction() throws Exception {
+ final ValueDescriptorImpl nodeDescriptor = (ValueDescriptorImpl)selectedNode.getDescriptor();
+ final Renderer lastRenderer = nodeDescriptor.getLastRenderer();
+ if (lastRenderer instanceof ArrayRenderer) {
+ selectedNode.setRenderer(clonedRenderer);
+ }
+ else if (lastRenderer instanceof CompoundNodeRenderer) {
+ final CompoundNodeRenderer compoundRenderer = (CompoundNodeRenderer)lastRenderer;
+ final ChildrenRenderer childrenRenderer = compoundRenderer.getChildrenRenderer();
+ if (childrenRenderer instanceof ExpressionChildrenRenderer) {
+ ExpressionChildrenRenderer.setPreferableChildrenRenderer(nodeDescriptor, clonedRenderer);
+ selectedNode.calcRepresentation();
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ boolean enable = false;
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(selectedNode != null) {
+ NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ enable = descriptor instanceof ValueDescriptorImpl && getArrayRenderer((ValueDescriptorImpl)descriptor) != null;
+ }
+ e.getPresentation().setVisible(enable);
+ }
+
+ @Nullable
+ private static ArrayRenderer getArrayRenderer(ValueDescriptorImpl descriptor) {
+ final Renderer lastRenderer = descriptor.getLastRenderer();
+ if (lastRenderer instanceof ArrayRenderer) {
+ return (ArrayRenderer)lastRenderer;
+ }
+ if (lastRenderer instanceof CompoundNodeRenderer && ((CompoundNodeRenderer)lastRenderer).getChildrenRenderer() instanceof ExpressionChildrenRenderer) {
+ final NodeRenderer lastChildrenRenderer = ExpressionChildrenRenderer.getLastChildrenRenderer(descriptor);
+ if (lastChildrenRenderer instanceof ArrayRenderer) {
+ return (ArrayRenderer)lastChildrenRenderer;
+ }
+ }
+ return null;
+ }
+
+ private static String createNodeTitle(String prefix, DebuggerTreeNodeImpl node) {
+ if (node != null) {
+ DebuggerTreeNodeImpl parent = node.getParent();
+ NodeDescriptorImpl descriptor = parent.getDescriptor();
+ if (descriptor instanceof ValueDescriptorImpl && ((ValueDescriptorImpl)descriptor).isArray()) {
+ int index = parent.getIndex(node);
+ return createNodeTitle(prefix, parent) + "[" + index + "]";
+ }
+ String name = (node.getDescriptor() != null)? node.getDescriptor().getName() : null;
+ return (name != null)? prefix + " " + name : prefix;
+ }
+ return prefix;
+ }
+
+ private static class NamedArrayConfigurable extends ArrayRendererConfigurable implements Configurable {
+ private final String myTitle;
+
+ public NamedArrayConfigurable(String title, ArrayRenderer renderer) {
+ super(renderer);
+ myTitle = title;
+ }
+
+ public String getDisplayName() {
+ return myTitle;
+ }
+
+ public String getHelpTopic() {
+ return null;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AutoRendererAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/AutoRendererAction.java
new file mode 100644
index 0000000..1891a77
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/AutoRendererAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+
+public class AutoRendererAction extends AnAction{
+ public void actionPerformed(AnActionEvent e) {
+ final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
+ final DebuggerTreeNodeImpl[] selectedNodes = DebuggerAction.getSelectedNodes(e.getDataContext());
+
+ if(debuggerContext != null && debuggerContext.getDebugProcess() != null) {
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public void threadAction() {
+ for (int i = 0; i < selectedNodes.length; i++) {
+ DebuggerTreeNodeImpl selectedNode = selectedNodes[i];
+ NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ if (descriptor instanceof ValueDescriptorImpl) {
+ ((ValueDescriptorImpl) descriptor).setRenderer(null);
+ selectedNode.calcRepresentation();
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/BaseValueAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/BaseValueAction.java
new file mode 100644
index 0000000..bd0373a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/BaseValueAction.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author max
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+/*
+ * @author Jeka
+ */
+public abstract class BaseValueAction extends DebuggerAction {
+
+ public void actionPerformed(AnActionEvent e) {
+ final DataContext actionContext = e.getDataContext();
+ final DebuggerTreeNodeImpl node = getSelectedNode(actionContext);
+ final Value value = getValue(node);
+ if (value == null) {
+ return;
+ }
+ final Project project = PlatformDataKeys.PROJECT.getData(actionContext);
+ final DebuggerManagerEx debuggerManager = DebuggerManagerEx.getInstanceEx(project);
+ if(debuggerManager == null) {
+ return;
+ }
+ final DebuggerContextImpl debuggerContext = debuggerManager.getContext();
+ if (debuggerContext == null || debuggerContext.getDebuggerSession() == null) {
+ return;
+ }
+
+ final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, project);
+ SuspendContextCommandImpl getTextCommand = new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+
+ public void contextAction() throws Exception {
+ //noinspection HardCodedStringLiteral
+ progressWindow.setText(DebuggerBundle.message("progress.evaluating", "toString()"));
+
+ final String valueAsString = DebuggerUtilsEx.getValueOrErrorAsString(debuggerContext.createEvaluationContext(), value);
+
+ if (progressWindow.isCanceled()) {
+ return;
+ }
+
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ String text = valueAsString;
+ if (text == null) {
+ text = "";
+ }
+ processText(project, text, node, debuggerContext);
+ }
+ });
+ }
+ };
+ progressWindow.setTitle(DebuggerBundle.message("title.evaluating"));
+ debuggerContext.getDebugProcess().getManagerThread().startProgress(getTextCommand, progressWindow);
+ }
+
+ protected abstract void processText(final Project project, String text, DebuggerTreeNodeImpl node, DebuggerContextImpl debuggerContext);
+
+ public void update(AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ Value value = getValue(getSelectedNode(e.getDataContext()));
+ presentation.setEnabled(value != null);
+ presentation.setVisible(value != null);
+ }
+
+ @Nullable
+ private static Value getValue(final DebuggerTreeNodeImpl node) {
+ if (node == null) {
+ return null;
+ }
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (!(descriptor instanceof ValueDescriptor)) {
+ return null;
+ }
+ return ((ValueDescriptor)descriptor).getValue();
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/CompareValueWithClipboardAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/CompareValueWithClipboardAction.java
new file mode 100644
index 0000000..0a4f2a3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/CompareValueWithClipboardAction.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.openapi.diff.DiffManager;
+import com.intellij.openapi.diff.actions.ClipboardVsValueContents;
+import com.intellij.openapi.project.Project;
+
+/**
+ * @author Jeka
+ */
+public class CompareValueWithClipboardAction extends BaseValueAction {
+ protected void processText(final Project project, final String text, DebuggerTreeNodeImpl node, DebuggerContextImpl debuggerContext) {
+ DiffManager.getInstance().getDiffTool().show(new ClipboardVsValueContents(text, project));
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/CopyValueAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/CopyValueAction.java
new file mode 100644
index 0000000..2565665
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/CopyValueAction.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.openapi.ide.CopyPasteManager;
+import com.intellij.openapi.project.Project;
+
+import java.awt.datatransfer.StringSelection;
+
+/*
+ * @author Jeka
+ */
+public class CopyValueAction extends BaseValueAction {
+ protected void processText(final Project project, final String text, DebuggerTreeNodeImpl node, DebuggerContextImpl debuggerContext) {
+ CopyPasteManager.getInstance().setContents(new StringSelection(text));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/CustomizeContextViewAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/CustomizeContextViewAction.java
new file mode 100644
index 0000000..4be7776
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/CustomizeContextViewAction.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.settings.DebuggerDataViewsConfigurable;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.settings.UserRenderersConfigurable;
+import com.intellij.debugger.ui.impl.FrameVariablesTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.options.CompositeConfigurable;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.TabbedConfigurable;
+import com.intellij.openapi.options.ex.SingleConfigurableEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 4:39:53 PM
+ */
+public class CustomizeContextViewAction extends DebuggerAction{
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+
+ Disposable disposable = Disposer.newDisposable();
+ final CompositeConfigurable configurable = new TabbedConfigurable(disposable) {
+ protected List<Configurable> createConfigurables() {
+ ArrayList<Configurable> array = new ArrayList<Configurable>();
+ array.add(new DebuggerDataViewsConfigurable(project));
+ array.add(new UserRenderersConfigurable(project));
+ return array;
+ }
+
+ public void apply() throws ConfigurationException {
+ super.apply();
+ NodeRendererSettings.getInstance().fireRenderersChanged();
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("title.customize.data.views");
+ }
+
+ public String getHelpTopic() {
+ return null;
+ }
+ };
+
+ SingleConfigurableEditor editor = new SingleConfigurableEditor(project, configurable);
+ Disposer.register(editor.getDisposable(), disposable);
+ editor.show();
+ }
+
+ public void update(AnActionEvent e) {
+ DebuggerTree tree = getTree(e.getDataContext());
+ e.getPresentation().setVisible(tree instanceof FrameVariablesTree);
+ e.getPresentation().setText(ActionsBundle.actionText(DebuggerActions.CUSTOMIZE_VIEWS));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/CustomizeThreadsViewAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/CustomizeThreadsViewAction.java
new file mode 100644
index 0000000..1681d68
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/CustomizeThreadsViewAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.settings.ThreadsViewConfigurable;
+import com.intellij.debugger.settings.ThreadsViewSettings;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.options.ex.SingleConfigurableEditor;
+import com.intellij.openapi.project.Project;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 4:40:12 PM
+ */
+public class CustomizeThreadsViewAction extends DebuggerAction {
+ public void actionPerformed(AnActionEvent e) {
+ Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ final SingleConfigurableEditor editor = new SingleConfigurableEditor(project, new ThreadsViewConfigurable(ThreadsViewSettings.getInstance()));
+ editor.show();
+ }
+
+ public void update(AnActionEvent e) {
+ e.getPresentation().setVisible(true);
+ e.getPresentation().setText(ActionsBundle.actionText(DebuggerActions.CUSTOMIZE_THREADS_VIEW));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java
new file mode 100644
index 0000000..8aebe9e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java
@@ -0,0 +1,156 @@
+/*
+ * 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 DebuggerAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.DebuggerTreePanel;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.DoubleClickListener;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class DebuggerAction extends AnAction {
+ private static final DebuggerTreeNodeImpl[] EMPTY_TREE_NODE_ARRAY = new DebuggerTreeNodeImpl[0];
+
+ @Nullable
+ public static DebuggerTree getTree(DataContext dataContext){
+ return DebuggerTree.DATA_KEY.getData(dataContext);
+ }
+
+ @Nullable
+ public static DebuggerTreePanel getPanel(DataContext dataContext){
+ return DebuggerTreePanel.DATA_KEY.getData(dataContext);
+ }
+
+ @Nullable
+ public static DebuggerTreeNodeImpl getSelectedNode(DataContext dataContext) {
+ DebuggerTree tree = getTree(dataContext);
+ if(tree == null) return null;
+
+ if (tree.getSelectionCount() != 1) {
+ return null;
+ }
+ TreePath path = tree.getSelectionPath();
+ if (path == null) {
+ return null;
+ }
+ Object component = path.getLastPathComponent();
+ if (!(component instanceof DebuggerTreeNodeImpl)) {
+ return null;
+ }
+ return (DebuggerTreeNodeImpl)component;
+ }
+
+ @Nullable
+ public static DebuggerTreeNodeImpl[] getSelectedNodes(DataContext dataContext) {
+ DebuggerTree tree = getTree(dataContext);
+ if(tree == null) return null;
+ TreePath[] paths = tree.getSelectionPaths();
+ if (paths == null || paths.length == 0) {
+ return EMPTY_TREE_NODE_ARRAY;
+ }
+ List<DebuggerTreeNodeImpl> nodes = new ArrayList<DebuggerTreeNodeImpl>(paths.length);
+ for (TreePath path : paths) {
+ Object component = path.getLastPathComponent();
+ if (component instanceof DebuggerTreeNodeImpl) {
+ nodes.add((DebuggerTreeNodeImpl) component);
+ }
+ }
+ return nodes.toArray(new DebuggerTreeNodeImpl[nodes.size()]);
+ }
+
+ public static DebuggerContextImpl getDebuggerContext(DataContext dataContext) {
+ DebuggerTreePanel panel = getPanel(dataContext);
+ if(panel != null) {
+ return panel.getContext();
+ } else {
+ Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+ return project != null ? (DebuggerManagerEx.getInstanceEx(project)).getContext() : DebuggerContextImpl.EMPTY_CONTEXT;
+ }
+ }
+
+ @Nullable
+ public static DebuggerStateManager getContextManager(DataContext dataContext) {
+ DebuggerTreePanel panel = getPanel(dataContext);
+ return panel == null ? null : panel.getContextManager();
+ }
+
+ public static boolean isContextView(AnActionEvent e) {
+ return DebuggerActions.EVALUATION_DIALOG_POPUP.equals(e.getPlace()) ||
+ DebuggerActions.FRAME_PANEL_POPUP.equals(e.getPlace()) ||
+ DebuggerActions.WATCH_PANEL_POPUP.equals(e.getPlace()) ||
+ DebuggerActions.INSPECT_PANEL_POPUP.equals(e.getPlace());
+ }
+
+ public static Disposable installEditAction(final JTree tree, String actionName) {
+ final DoubleClickListener listener = new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ if (tree.getPathForLocation(e.getX(), e.getY()) == null) return false;
+ DataContext dataContext = DataManager.getInstance().getDataContext(tree);
+ GotoFrameSourceAction.doAction(dataContext);
+ return true;
+ }
+ };
+ listener.installOn(tree);
+
+ final AnAction action = ActionManager.getInstance().getAction(actionName);
+ action.registerCustomShortcutSet(CommonShortcuts.getEditSource(), tree);
+
+ return new Disposable() {
+ public void dispose() {
+ listener.uninstall(tree);
+ action.unregisterCustomShortcutSet(tree);
+ }
+ };
+ }
+
+ public static boolean isFirstStart(final AnActionEvent event) {
+ //noinspection HardCodedStringLiteral
+ String key = "initalized";
+ if(event.getPresentation().getClientProperty(key) != null) return false;
+
+ event.getPresentation().putClientProperty(key, key);
+ return true;
+ }
+
+ public static void enableAction(final AnActionEvent event, final boolean enable) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ event.getPresentation().setEnabled(enable);
+ event.getPresentation().setVisible(true);
+ }
+ });
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java
new file mode 100644
index 0000000..ca323d4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/*
+ * Interface DebuggerActions
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.ui.impl.DebuggerTreePanel;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.xdebugger.impl.actions.XDebuggerActions;
+import org.jetbrains.annotations.NonNls;
+
+public interface DebuggerActions extends XDebuggerActions {
+ @NonNls String POP_FRAME = "Debugger.PopFrame";
+ @NonNls String EVALUATION_DIALOG_POPUP = "Debugger.EvaluationDialogPopup";
+ @NonNls String FRAME_PANEL_POPUP = "Debugger.FramePanelPopup";
+ @NonNls String INSPECT_PANEL_POPUP = "Debugger.InspectPanelPopup";
+ @NonNls String THREADS_PANEL_POPUP = "Debugger.ThreadsPanelPopup";
+ @NonNls String WATCH_PANEL_POPUP = "Debugger.WatchesPanelPopup";
+ @Deprecated @NonNls String DEBUGGER_TREE = DebuggerTree.DATA_KEY.getName();
+ @Deprecated @NonNls String DEBUGGER_TREE_PANEL = DebuggerTreePanel.DATA_KEY.getName();
+ @NonNls String REMOVE_WATCH = "Debugger.RemoveWatch";
+ @NonNls String NEW_WATCH = "Debugger.NewWatch";
+ @NonNls String EDIT_WATCH = "Debugger.EditWatch";
+ @NonNls String COPY_VALUE = "Debugger.CopyValue";
+ @NonNls String SET_VALUE = "Debugger.SetValue";
+ @NonNls String EDIT_FRAME_SOURCE = "Debugger.EditFrameSource";
+ @NonNls String EDIT_NODE_SOURCE = "Debugger.EditNodeSource";
+ @NonNls String REPRESENTATION_LIST = "Debugger.Representation";
+ @NonNls String CUSTOMIZE_VIEWS = "Debugger.CustomizeContextView";
+ @NonNls String CUSTOMIZE_THREADS_VIEW = "Debugger.CustomizeThreadsView";
+ @NonNls String INSPECT = "Debugger.Inspect";
+ @NonNls String EXPORT_THREADS = "ExportThreads";
+ @NonNls String DUMP_THREADS = "DumpThreads";
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerKeymapExtension.java b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerKeymapExtension.java
new file mode 100644
index 0000000..c200cb1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerKeymapExtension.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.keymap.KeyMapBundle;
+import com.intellij.openapi.keymap.KeymapExtension;
+import com.intellij.openapi.keymap.KeymapGroup;
+import com.intellij.openapi.keymap.impl.ui.Group;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * @author yole
+ */
+public class DebuggerKeymapExtension implements KeymapExtension {
+ public KeymapGroup createGroup(final Condition<AnAction> filtered, final Project project) {
+ ActionManager actionManager = ActionManager.getInstance();
+ DefaultActionGroup debuggerGroup = (DefaultActionGroup)actionManager.getActionOrStub(IdeActions.GROUP_DEBUGGER);
+ AnAction[] debuggerActions = debuggerGroup.getChildActionsOrStubs();
+
+ ArrayList<String> ids = new ArrayList<String>();
+ for (AnAction debuggerAction : debuggerActions) {
+ String actionId = debuggerAction instanceof ActionStub ? ((ActionStub)debuggerAction).getId() : actionManager.getId(debuggerAction);
+ if (filtered == null || filtered.value(debuggerAction)) {
+ ids.add(actionId);
+ }
+ }
+
+ Collections.sort(ids);
+ Group group = new Group(KeyMapBundle.message("debugger.actions.group.title"), IdeActions.GROUP_DEBUGGER, null);
+ for (String id : ids) {
+ group.addActionId(id);
+ }
+
+ return group;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/EditFrameSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/EditFrameSourceAction.java
new file mode 100644
index 0000000..ba02df4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/EditFrameSourceAction.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.IdeActions;
+
+/**
+ * @author lex
+ */
+public class EditFrameSourceAction extends GotoFrameSourceAction{
+ public void update(AnActionEvent e) {
+ super.update(e);
+ e.getPresentation().setText(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getTemplatePresentation().getText());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java
new file mode 100644
index 0000000..b009370
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.expression.Modifier;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+
+public class EditSourceAction extends DebuggerAction{
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+
+ if(project == null) return;
+
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ final DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(debuggerContext != null && selectedNode != null) {
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public void threadAction() {
+ final SourcePosition sourcePosition = getSourcePosition(selectedNode, debuggerContext);
+ if (sourcePosition != null) {
+ sourcePosition.navigate(true);
+ }
+ }
+ });
+ }
+ }
+
+ private SourcePosition getSourcePosition(DebuggerTreeNodeImpl selectedNode, DebuggerContextImpl debuggerContext) {
+ DebuggerTreeNodeImpl node = selectedNode;
+ final DebuggerContextImpl context = debuggerContext;
+
+ if(node == null) return null;
+ if(context == null) return null;
+
+ final Project project = context.getProject();
+
+ final DebuggerSession debuggerSession = context.getDebuggerSession();
+
+ if(debuggerSession == null) return null;
+
+ NodeDescriptorImpl nodeDescriptor = node.getDescriptor();
+ if(nodeDescriptor instanceof WatchItemDescriptor) {
+ Modifier modifier = ((WatchItemDescriptor)nodeDescriptor).getModifier();
+
+ if(modifier == null) return null;
+
+ nodeDescriptor = (NodeDescriptorImpl)modifier.getInspectItem(project);
+ }
+
+ final NodeDescriptorImpl nodeDescriptor1 = nodeDescriptor;
+ return ApplicationManager.getApplication().runReadAction(new Computable<SourcePosition>() {
+ public SourcePosition compute() {
+ if (nodeDescriptor1 instanceof FieldDescriptorImpl && debuggerSession != null) {
+ return ((FieldDescriptorImpl)nodeDescriptor1).getSourcePosition(project, context);
+ }
+ if (nodeDescriptor1 instanceof LocalVariableDescriptorImpl && debuggerSession != null) {
+ return ((LocalVariableDescriptorImpl)nodeDescriptor1).getSourcePosition(project, context);
+ }
+ return null;
+ }
+ });
+ }
+
+ public void update(AnActionEvent e) {
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ final DebuggerTreeNodeImpl node = getSelectedNode(e.getDataContext());
+
+ final Presentation presentation = e.getPresentation();
+
+ presentation.setEnabled(true);
+
+ //if user used shortcut actionPerformed is called immediately after update
+ //we not disable presentation here to allow actionPerform work
+ DebuggerInvocationUtil.invokeLater(project, new Runnable() {
+ public void run() {
+ presentation.setEnabled(false);
+ }
+ });
+
+ if(debuggerContext != null && debuggerContext.getDebugProcess() != null) {
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public void threadAction() {
+ final SourcePosition position = getSourcePosition(node, debuggerContext);
+ if (position != null) {
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ presentation.setEnabled(true);
+ }
+ });
+ }
+ }
+ });
+ }
+
+ e.getPresentation().setText(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getTemplatePresentation().getText());
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/EditWatchAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/EditWatchAction.java
new file mode 100644
index 0000000..1654287
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/EditWatchAction.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.debugger.ui.impl.MainWatchPanel;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 8:34:01 PM
+ */
+public class EditWatchAction extends DebuggerAction {
+ public void actionPerformed(final AnActionEvent e) {
+ final DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(selectedNode == null || !(selectedNode.getDescriptor() instanceof WatchItemDescriptor)) return;
+
+ Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+
+ MainWatchPanel watchPanel = DebuggerPanelsManager.getInstance(project).getWatchPanel();
+ if(watchPanel != null) {
+ watchPanel.editNode(selectedNode);
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ final DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+
+ e.getPresentation().setVisible(selectedNode != null && selectedNode.getDescriptor() instanceof WatchItemDescriptor);
+ }
+
+};
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/EvaluateActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/EvaluateActionHandler.java
new file mode 100644
index 0000000..22694cb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/EvaluateActionHandler.java
@@ -0,0 +1,143 @@
+/*
+ * 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 EvaluateAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.ExpressionEvaluationDialog;
+import com.intellij.debugger.ui.StatementEvaluationDialog;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeExpression;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.DataKeys;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import org.jetbrains.annotations.NotNull;
+
+public class EvaluateActionHandler extends DebuggerActionHandler {
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ DebuggerContextImpl context = DebuggerAction.getDebuggerContext(event.getDataContext());
+
+ if(context != null) {
+ DebuggerSession debuggerSession = context.getDebuggerSession();
+ return debuggerSession != null && debuggerSession.isPaused();
+ }
+
+ return false;
+ }
+
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ final DataContext dataContext = event.getDataContext();
+ final DebuggerContextImpl context = DebuggerAction.getDebuggerContext(dataContext);
+
+ if(context == null) {
+ return;
+ }
+
+ final Editor editor = event.getData(DataKeys.EDITOR);
+
+ TextWithImports editorText = DebuggerUtilsEx.getEditorText(editor);
+ if (editorText == null) {
+ final DebuggerTreeNodeImpl selectedNode = DebuggerAction.getSelectedNode(dataContext);
+ final String actionName = event.getPresentation().getText();
+
+ if (selectedNode != null && selectedNode.getDescriptor() instanceof ValueDescriptorImpl) {
+ context.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(context) {
+ public void threadAction() {
+ try {
+ final TextWithImports evaluationText = DebuggerTreeNodeExpression.createEvaluationText(selectedNode, context);
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ showEvaluationDialog(project, evaluationText);
+ }
+ });
+ }
+ catch (final EvaluateException e1) {
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ Messages.showErrorDialog(project, e1.getMessage(), actionName);
+ }
+ });
+ }
+ }
+
+ protected void commandCancelled() {
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ if(selectedNode.getDescriptor() instanceof WatchItemDescriptor) {
+ try {
+ TextWithImports editorText = DebuggerTreeNodeExpression.createEvaluationText(selectedNode, context);
+ showEvaluationDialog(project, editorText);
+ }
+ catch (EvaluateException e1) {
+ Messages.showErrorDialog(project, e1.getMessage(), actionName);
+ }
+ }
+ }
+ });
+ }
+ });
+ return;
+ }
+ }
+
+ showEvaluationDialog(project, editorText);
+ }
+
+ public static void showEvaluationDialog(Project project, TextWithImports defaultExpression, String dialogType) {
+ if(defaultExpression == null) {
+ defaultExpression = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "");
+ }
+
+ CodeFragmentKind kind = DebuggerSettings.EVALUATE_FRAGMENT.equals(dialogType) ? CodeFragmentKind.CODE_BLOCK : CodeFragmentKind.EXPRESSION;
+
+ DebuggerSettings.getInstance().EVALUATION_DIALOG_TYPE = dialogType;
+ TextWithImportsImpl text = new TextWithImportsImpl(kind, defaultExpression.getText(), defaultExpression.getImports(), defaultExpression.getFileType());
+
+ final DialogWrapper dialog;
+ if(DebuggerSettings.EVALUATE_FRAGMENT.equals(dialogType)) {
+ dialog = new StatementEvaluationDialog(project, text);
+ }
+ else {
+ dialog = new ExpressionEvaluationDialog(project, text);
+ }
+
+ dialog.show();
+ }
+
+ public static void showEvaluationDialog(Project project, TextWithImports text) {
+ showEvaluationDialog(project, text, DebuggerSettings.getInstance().EVALUATION_DIALOG_TYPE);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ExportThreadsAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ExportThreadsAction.java
new file mode 100644
index 0000000..e7b43d1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ExportThreadsAction.java
@@ -0,0 +1,89 @@
+/*
+ * 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 ExportThreadsAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.ExportDialog;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.SystemProperties;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class ExportThreadsAction extends AnAction implements AnAction.TransparentUpdate {
+ public void actionPerformed(AnActionEvent e) {
+ Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ if (project == null) {
+ return;
+ }
+ DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+
+ if(context.getDebuggerSession() != null) {
+ String destinationDirectory = "";
+ final VirtualFile baseDir = project.getBaseDir();
+ if (baseDir != null) destinationDirectory = baseDir.getPresentableUrl();
+
+ ExportDialog dialog = new ExportDialog(context.getDebugProcess(), destinationDirectory);
+ dialog.show();
+ if (dialog.isOK()) {
+ try {
+ File file = new File(dialog.getFilePath());
+ BufferedWriter writer = new BufferedWriter(new FileWriter(file));
+ try {
+ String text = StringUtil.convertLineSeparators(dialog.getTextToSave(), SystemProperties.getLineSeparator());
+ writer.write(text);
+ }
+ finally {
+ writer.close();
+ }
+ }
+ catch (IOException ex) {
+ Messages.showMessageDialog(project, ex.getMessage(), ActionsBundle.actionText(DebuggerActions.EXPORT_THREADS), Messages.getErrorIcon());
+ }
+ }
+ }
+ }
+
+
+
+ public void update(AnActionEvent event){
+ Presentation presentation = event.getPresentation();
+ Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
+ if (project == null) {
+ presentation.setEnabled(false);
+ return;
+ }
+ DebuggerSession debuggerSession = (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession();
+ presentation.setEnabled(debuggerSession != null && debuggerSession.isPaused());
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ForceRunToCursorActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/ForceRunToCursorActionHandler.java
new file mode 100644
index 0000000..553b5d4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ForceRunToCursorActionHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Sep 12, 2006
+ */
+public class ForceRunToCursorActionHandler extends RunToCursorActionHandler {
+ public ForceRunToCursorActionHandler() {
+ super(true);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ForceStepIntoActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/ForceStepIntoActionHandler.java
new file mode 100644
index 0000000..dc74274
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ForceStepIntoActionHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class ForceStepIntoActionHandler extends AbstractSteppingActionHandler {
+ public void perform(@NotNull final Project project, AnActionEvent e) {
+ final DebuggerSession session = getSession(project);
+ if (session != null) {
+ session.stepInto(true, null);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ForceStepOverActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/ForceStepOverActionHandler.java
new file mode 100644
index 0000000..613fcf0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ForceStepOverActionHandler.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class ForceStepOverActionHandler extends AbstractSteppingActionHandler {
+ public void perform(@NotNull final Project project, AnActionEvent e) {
+ final DebuggerSession session = getSession(project);
+ if (session != null) {
+ session.stepOver(true);
+ }
+ }
+}
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/FreezeThreadAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/FreezeThreadAction.java
new file mode 100644
index 0000000..eb6dea3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/FreezeThreadAction.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+
+/**
+ * @author lex
+ */
+public class FreezeThreadAction extends DebuggerAction {
+ public void actionPerformed(final AnActionEvent e) {
+ DebuggerTreeNodeImpl[] selectedNode = getSelectedNodes(e.getDataContext());
+ if (selectedNode == null) {
+ return;
+ }
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+
+ for (final DebuggerTreeNodeImpl debuggerTreeNode : selectedNode) {
+ ThreadDescriptorImpl threadDescriptor = ((ThreadDescriptorImpl)debuggerTreeNode.getDescriptor());
+ final ThreadReferenceProxyImpl thread = threadDescriptor.getThreadReference();
+
+ if (!threadDescriptor.isFrozen()) {
+ debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
+ public void contextAction() throws Exception {
+ debugProcess.createFreezeThreadCommand(thread).run();
+ debuggerTreeNode.calcValue();
+ }
+ });
+ }
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ DebuggerTreeNodeImpl[] selectedNode = getSelectedNodes(e.getDataContext());
+ if (selectedNode == null) {
+ return;
+ }
+ DebugProcessImpl debugProcess = getDebuggerContext(e.getDataContext()).getDebugProcess();
+
+ boolean visible = false;
+ if (debugProcess != null) {
+ visible = true;
+ for (DebuggerTreeNodeImpl aSelectedNode : selectedNode) {
+ NodeDescriptorImpl threadDescriptor = aSelectedNode.getDescriptor();
+ if (!(threadDescriptor instanceof ThreadDescriptorImpl) || ((ThreadDescriptorImpl)threadDescriptor).isFrozen()) {
+ visible = false;
+ break;
+ }
+ }
+
+ }
+
+ e.getPresentation().setVisible(visible);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/GotoFrameSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/GotoFrameSourceAction.java
new file mode 100644
index 0000000..e13a0eb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/GotoFrameSourceAction.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerContextUtil;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+
+/**
+ * @author lex
+ */
+public abstract class GotoFrameSourceAction extends DebuggerAction{
+ public void actionPerformed(AnActionEvent e) {
+ DataContext dataContext = e.getDataContext();
+ doAction(dataContext);
+ }
+
+ protected static void doAction(DataContext dataContext) {
+ final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+ if(project == null) return;
+ StackFrameDescriptorImpl stackFrameDescriptor = getStackFrameDescriptor(dataContext);
+ if(stackFrameDescriptor != null) {
+ DebuggerContextUtil.setStackFrame(getContextManager(dataContext), stackFrameDescriptor.getFrameProxy());
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ e.getPresentation().setVisible(getStackFrameDescriptor(e.getDataContext()) != null);
+ }
+
+ private static StackFrameDescriptorImpl getStackFrameDescriptor(DataContext dataContext) {
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(dataContext);
+ if(selectedNode == null) return null;
+ if(selectedNode.getDescriptor() == null || !(selectedNode.getDescriptor() instanceof StackFrameDescriptorImpl)) return null;
+ return (StackFrameDescriptorImpl)selectedNode.getDescriptor();
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/HotSwapAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/HotSwapAction.java
new file mode 100644
index 0000000..250ae85
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/HotSwapAction.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.HotSwapUI;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+
+/**
+ * @author lex
+ */
+public class HotSwapAction extends AnAction{
+
+ public void actionPerformed(AnActionEvent e) {
+ DataContext dataContext = e.getDataContext();
+ Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+ if(project == null) {
+ return;
+ }
+
+ DebuggerManagerEx debuggerManager = DebuggerManagerEx.getInstanceEx(project);
+ DebuggerSession session = debuggerManager.getContext().getDebuggerSession();
+
+ if(session != null && session.isAttached()) {
+ HotSwapUI.getInstance(project).reloadChangedClasses(session, DebuggerSettings.getInstance().COMPILE_BEFORE_HOTSWAP);
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ DataContext dataContext = e.getDataContext();
+ Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+ if(project == null) {
+ e.getPresentation().setEnabled(false);
+ return;
+ }
+
+ DebuggerManagerEx debuggerManager = DebuggerManagerEx.getInstanceEx(project);
+ DebuggerSession session = debuggerManager.getContext().getDebuggerSession();
+
+ e.getPresentation().setEnabled(session != null && session.isAttached() && session.getProcess().canRedefineClasses());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/InspectAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/InspectAction.java
new file mode 100644
index 0000000..36bc1b7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/InspectAction.java
@@ -0,0 +1,129 @@
+/*
+ * 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 InspectAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.expression.Modifier;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.InspectDialog;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.sun.jdi.Field;
+
+public class InspectAction extends DebuggerAction {
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ final DebuggerTreeNodeImpl node = getSelectedNode(e.getDataContext());
+ if(node == null) return;
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ final DebuggerStateManager stateManager = getContextManager(e.getDataContext());
+ if(!(descriptor instanceof ValueDescriptorImpl) || stateManager == null) return;
+ final DebuggerContextImpl context = stateManager.getContext();
+
+ if (!canInspect((ValueDescriptorImpl)descriptor, context)) {
+ return;
+ }
+ context.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(context) {
+ public void threadAction() {
+ try {
+ final TextWithImports evaluationText = DebuggerTreeNodeExpression.createEvaluationText(node, context);
+
+ final NodeDescriptorImpl inspectDescriptor;
+ if (descriptor instanceof WatchItemDescriptor) {
+ inspectDescriptor = (NodeDescriptorImpl) ((WatchItemDescriptor) descriptor).getModifier().getInspectItem(project);
+ }
+ else {
+ inspectDescriptor = descriptor;
+ }
+
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ final InspectDialog dialog = new InspectDialog(project, stateManager, ActionsBundle.actionText(DebuggerActions.INSPECT) + " '" + evaluationText + "'", inspectDescriptor);
+ dialog.show();
+ }
+ });
+ }
+ catch (final EvaluateException e1) {
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ Messages.showErrorDialog(project, e1.getMessage(), ActionsBundle.actionText(DebuggerActions.INSPECT));
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private boolean canInspect(ValueDescriptorImpl descriptor, DebuggerContextImpl context) {
+ DebuggerSession session = context.getDebuggerSession();
+ if (session == null || !session.isPaused()) return false;
+
+ boolean isField = descriptor instanceof FieldDescriptorImpl;
+
+ if(descriptor instanceof WatchItemDescriptor) {
+ Modifier modifier = ((WatchItemDescriptor)descriptor).getModifier();
+ if(modifier == null || !modifier.canInspect()) return false;
+ isField = modifier instanceof Field;
+ }
+
+ if (isField) { // check if possible
+ if (!context.getDebugProcess().canWatchFieldModification()) {
+ Messages.showMessageDialog(
+ context.getProject(),
+ DebuggerBundle.message("error.modification.watchpoints.not.supported"),
+ ActionsBundle.actionText(DebuggerActions.INSPECT),
+ Messages.getInformationIcon()
+ );
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void update(AnActionEvent e) {
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ boolean enabled = false;
+ if(selectedNode != null) {
+ NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ if(descriptor != null) {
+ if(descriptor instanceof LocalVariableDescriptorImpl || descriptor instanceof FieldDescriptorImpl || descriptor instanceof ArrayElementDescriptorImpl) {
+ enabled = true;
+ }
+ else if(descriptor instanceof WatchItemDescriptor){
+ Modifier modifier = ((WatchItemDescriptor)descriptor).getModifier();
+ enabled = modifier != null && modifier.canInspect();
+ }
+ }
+ }
+ e.getPresentation().setVisible(enabled);
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java
new file mode 100644
index 0000000..62e861d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2010 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 7:35:09 PM
+ */
+public class InterruptThreadAction extends DebuggerAction{
+
+ public void actionPerformed(final AnActionEvent e) {
+ final DebuggerTreeNodeImpl[] nodes = getSelectedNodes(e.getDataContext());
+ if (nodes == null) {
+ return;
+ }
+
+ //noinspection ConstantConditions
+ final List<ThreadReferenceProxyImpl> threadsToInterrupt = new ArrayList<ThreadReferenceProxyImpl>();
+ for (final DebuggerTreeNodeImpl debuggerTreeNode : nodes) {
+ final NodeDescriptorImpl descriptor = debuggerTreeNode.getDescriptor();
+ if (descriptor instanceof ThreadDescriptorImpl) {
+ threadsToInterrupt.add(((ThreadDescriptorImpl)descriptor).getThreadReference());
+ }
+ }
+
+ if (!threadsToInterrupt.isEmpty()) {
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ for (ThreadReferenceProxyImpl thread : threadsToInterrupt) {
+ thread.getThreadReference().interrupt();
+ }
+ }
+ });
+ }
+
+ }
+
+ public void update(AnActionEvent e) {
+ final DebuggerTreeNodeImpl[] selectedNodes = getSelectedNodes(e.getDataContext());
+
+ boolean visible = false;
+ boolean enabled = false;
+
+ if(selectedNodes != null && selectedNodes.length > 0){
+ visible = true;
+ enabled = true;
+ for (DebuggerTreeNodeImpl selectedNode : selectedNodes) {
+ final NodeDescriptorImpl threadDescriptor = selectedNode.getDescriptor();
+ if (!(threadDescriptor instanceof ThreadDescriptorImpl)) {
+ visible = false;
+ break;
+ }
+ }
+
+ if (visible) {
+ for (DebuggerTreeNodeImpl selectedNode : selectedNodes) {
+ final ThreadDescriptorImpl threadDescriptor = (ThreadDescriptorImpl)selectedNode.getDescriptor();
+ if (threadDescriptor.isFrozen()) {
+ enabled = false;
+ break;
+ }
+ }
+ }
+ }
+ final Presentation presentation = e.getPresentation();
+ presentation.setText(DebuggerBundle.message("action.interrupt.thread.text"));
+ presentation.setVisible(visible);
+ presentation.setEnabled(enabled);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaEditBreakpointActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaEditBreakpointActionHandler.java
new file mode 100644
index 0000000..cf03ffc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaEditBreakpointActionHandler.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointFactory;
+import com.intellij.debugger.ui.breakpoints.BreakpointPropertiesPanel;
+import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupListener;
+import com.intellij.openapi.ui.popup.LightweightWindowEvent;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.impl.actions.EditBreakpointActionHandler;
+import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointsMasterDetailPopupFactory;
+import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: zajac
+ * Date: 04.05.12
+ * Time: 4:10
+ * To change this template use File | Settings | File Templates.
+ */
+public class JavaEditBreakpointActionHandler extends EditBreakpointActionHandler {
+ @Override
+ protected void doShowPopup(final Project project, final EditorGutterComponentEx gutterComponent, final Point whereToShow, final Object breakpoint) {
+ if (!(breakpoint instanceof BreakpointWithHighlighter)) return;
+
+ final BreakpointWithHighlighter javaBreakpoint = (BreakpointWithHighlighter)breakpoint;
+ Key<? extends BreakpointWithHighlighter> category = javaBreakpoint.getCategory();
+
+ final BreakpointFactory[] allFactories = ApplicationManager.getApplication().getExtensions(BreakpointFactory.EXTENSION_POINT_NAME);
+ BreakpointFactory breakpointFactory = null;
+ for (BreakpointFactory factory : allFactories) {
+ if (factory.getBreakpointCategory().equals(category)) {
+ breakpointFactory = factory;
+ }
+ }
+ assert breakpointFactory != null : "can't find factory for breakpoint " + javaBreakpoint;
+
+ final BreakpointPropertiesPanel propertiesPanel = breakpointFactory.createBreakpointPropertiesPanel(project, true);
+ propertiesPanel.initFrom(javaBreakpoint, false);
+
+ final JComponent mainPanel = propertiesPanel.getPanel();
+ final String displayName = javaBreakpoint.getDisplayName();
+
+ final JBPopupListener saveOnClose = new JBPopupListener() {
+ @Override
+ public void beforeShown(LightweightWindowEvent event) {
+ }
+
+ @Override
+ public void onClosed(LightweightWindowEvent event) {
+ propertiesPanel.saveTo(javaBreakpoint, new Runnable() {
+ @Override
+ public void run() {
+ }
+ });
+ }
+ };
+
+ final Runnable showMoreOptions = new Runnable() {
+ @Override
+ public void run() {
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ final JBPopup popup = BreakpointsMasterDetailPopupFactory.
+ getInstance(project).createPopup(javaBreakpoint);
+ if (popup != null) {
+ popup.showCenteredInCurrentWindow(project);
+ }
+ }
+ });
+ }
+ };
+ final Balloon balloon = DebuggerUIUtil.showBreakpointEditor(project, mainPanel, displayName, whereToShow, gutterComponent, showMoreOptions,
+ breakpoint);
+ balloon.addListener(saveOnClose);
+
+ propertiesPanel.setDelegate(new BreakpointPropertiesPanel.Delegate() {
+ @Override
+ public void showActionsPanel() {
+ propertiesPanel.setActionsPanelVisible(true);
+ balloon.hide();
+ final Balloon newBalloon =
+ DebuggerUIUtil.showBreakpointEditor(project, mainPanel, displayName, whereToShow, gutterComponent, showMoreOptions, breakpoint);
+ newBalloon.addListener(saveOnClose);
+ }
+ });
+
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ IdeFocusManager.findInstance().requestFocus(mainPanel, true);
+ }
+ });
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull Project project, AnActionEvent event) {
+ DataContext dataContext = event.getDataContext();
+ Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
+ if (editor == null) {
+ return false;
+ }
+ final Pair<GutterIconRenderer,Object> pair = XBreakpointUtil.findSelectedBreakpoint(project, editor);
+ return pair.first != null && pair.second instanceof BreakpointWithHighlighter;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java
new file mode 100644
index 0000000..dcd0454
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.impl.tree.TreeBuilder;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.util.containers.HashMap;
+import com.intellij.xdebugger.impl.actions.MarkObjectActionHandler;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/*
+ * Class SetValueAction
+ * @author Jeka
+ */
+public class JavaMarkObjectActionHandler extends MarkObjectActionHandler {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.actions.JavaMarkObjectActionHandler");
+ public static final long AUTO_MARKUP_REFERRING_OBJECTS_LIMIT = 100L; // todo: some reasonable limit
+
+ @Override
+ public void perform(@NotNull Project project, AnActionEvent event) {
+ final DebuggerTreeNodeImpl node = DebuggerAction.getSelectedNode(event.getDataContext());
+ if (node == null) {
+ return;
+ }
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (!(descriptor instanceof ValueDescriptorImpl)) {
+ return;
+ }
+
+ final DebuggerTree tree = node.getTree();
+ tree.saveState(node);
+
+ final ValueDescriptorImpl valueDescriptor = ((ValueDescriptorImpl)descriptor);
+ final DebuggerContextImpl debuggerContext = tree.getDebuggerContext();
+ final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+ final ValueMarkup markup = valueDescriptor.getMarkup(debugProcess);
+ debugProcess.getManagerThread().invoke(new DebuggerContextCommandImpl(debuggerContext) {
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+ public void threadAction() {
+ boolean sessionRefreshNeeded = true;
+ try {
+ if (markup != null) {
+ valueDescriptor.setMarkup(debugProcess, null);
+ }
+ else {
+ final String defaultText = valueDescriptor.getName();
+ final Ref<Pair<ValueMarkup,Boolean>> result = new Ref<Pair<ValueMarkup, Boolean>>(null);
+ try {
+ final boolean suggestAdditionalMarkup = canSuggestAdditionalMarkup(debugProcess, valueDescriptor.getValue());
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ ObjectMarkupPropertiesDialog dialog = new ObjectMarkupPropertiesDialog(defaultText, suggestAdditionalMarkup);
+ dialog.show();
+ if (dialog.isOK()) {
+ result.set(Pair.create(dialog.getConfiguredMarkup(), dialog.isMarkAdditionalFields()));
+ }
+ }
+ });
+ }
+ catch (InterruptedException ignored) {
+ }
+ catch (InvocationTargetException e) {
+ LOG.error(e);
+ }
+ final Pair<ValueMarkup, Boolean> pair = result.get();
+ if (pair != null) {
+ valueDescriptor.setMarkup(debugProcess, pair.first);
+ if (pair.second) {
+ final Value value = valueDescriptor.getValue();
+ final Map<ObjectReference, ValueMarkup> additionalMarkup = suggestMarkup((ObjectReference)value);
+ if (!additionalMarkup.isEmpty()) {
+ final Map<ObjectReference, ValueMarkup> map = NodeDescriptorImpl.getMarkupMap(debugProcess);
+ if (map != null) {
+ for (Map.Entry<ObjectReference, ValueMarkup> entry : additionalMarkup.entrySet()) {
+ final ObjectReference key = entry.getKey();
+ if (!map.containsKey(key)) {
+ map.put(key, entry.getValue());
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ sessionRefreshNeeded = false;
+ }
+ }
+ }
+ finally {
+ final boolean _sessionRefreshNeeded = sessionRefreshNeeded;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ tree.restoreState(node);
+ final TreeBuilder model = tree.getMutableModel();
+ refreshLabelsRecursively(model.getRoot(), model, valueDescriptor.getValue());
+ if (_sessionRefreshNeeded) {
+ final DebuggerSession session = debuggerContext.getDebuggerSession();
+ if (session != null) {
+ session.refresh(true);
+ }
+ }
+ }
+
+ private void refreshLabelsRecursively(Object node, TreeBuilder model, Value value) {
+ if (node instanceof DebuggerTreeNodeImpl) {
+ final DebuggerTreeNodeImpl _node = (DebuggerTreeNodeImpl)node;
+ final NodeDescriptorImpl descriptor = _node.getDescriptor();
+ if (descriptor instanceof ValueDescriptor && Comparing.equal(value, ((ValueDescriptor)descriptor).getValue())) {
+ _node.labelChanged();
+ }
+ }
+ final int childCount = model.getChildCount(node);
+ for (int idx = 0; idx < childCount; idx++) {
+ refreshLabelsRecursively(model.getChild(node, idx), model, value);
+ }
+ }
+
+ });
+ }
+ }
+ });
+ }
+
+ private static boolean canSuggestAdditionalMarkup(DebugProcessImpl debugProcess, Value value) {
+ if (!debugProcess.getVirtualMachineProxy().canGetInstanceInfo()) {
+ return false;
+ }
+ if (!(value instanceof ObjectReference)) {
+ return false;
+ }
+ final ObjectReference objRef = (ObjectReference)value;
+ if (objRef instanceof ArrayReference || objRef instanceof ClassObjectReference || objRef instanceof ThreadReference || objRef instanceof ThreadGroupReference || objRef instanceof ClassLoaderReference) {
+ return false;
+ }
+ return true;
+ }
+
+ private static Map<ObjectReference, ValueMarkup> suggestMarkup(ObjectReference objRef) {
+ final Map<ObjectReference, ValueMarkup> result = new HashMap<ObjectReference, ValueMarkup>();
+ for (ObjectReference ref : getReferringObjects(objRef)) {
+ if (!(ref instanceof ClassObjectReference)) {
+ // consider references from statisc fields only
+ continue;
+ }
+ final ReferenceType refType = ((ClassObjectReference)ref).reflectedType();
+ if (!refType.isAbstract()) {
+ continue;
+ }
+ for (Field field : refType.visibleFields()) {
+ if (!(field.isStatic() && field.isFinal())) {
+ continue;
+ }
+ if (DebuggerUtils.isPrimitiveType(field.typeName())) {
+ continue;
+ }
+ final Value fieldValue = refType.getValue(field);
+ if (!(fieldValue instanceof ObjectReference)) {
+ continue;
+ }
+ final ValueMarkup markup = result.get((ObjectReference)fieldValue);
+
+ final String fieldName = field.name();
+ final Color autoMarkupColor = getAutoMarkupColor();
+ if (markup == null) {
+ result.put((ObjectReference)fieldValue, new ValueMarkup(fieldName, autoMarkupColor, createMarkupTooltipText(null, refType, fieldName)));
+ }
+ else {
+ final String currentText = markup.getText();
+ if (!currentText.contains(fieldName)) {
+ final String currentTooltip = markup.getToolTipText();
+ final String tooltip = createMarkupTooltipText(currentTooltip, refType, fieldName);
+ result.put((ObjectReference)fieldValue, new ValueMarkup(currentText + ", " + fieldName, autoMarkupColor, tooltip));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List<ObjectReference> getReferringObjects(ObjectReference value) {
+ try {
+ return value.referringObjects(AUTO_MARKUP_REFERRING_OBJECTS_LIMIT);
+ }
+ catch (UnsupportedOperationException e) {
+ LOG.info(e);
+ }
+ return Collections.emptyList();
+ }
+
+ private static String createMarkupTooltipText(@Nullable String prefix, ReferenceType refType, String fieldName) {
+ final StringBuilder builder = new StringBuilder();
+ if (prefix == null) {
+ builder.append("Value referenced from:");
+ }
+ else {
+ builder.append(prefix);
+ }
+ return builder.append("<br><b>").append(refType.name()).append(".").append(fieldName).append("</b>").toString();
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull Project project, AnActionEvent event) {
+ final DebuggerTreeNodeImpl node = DebuggerAction.getSelectedNode(event.getDataContext());
+ return node != null && node.getDescriptor() instanceof ValueDescriptor;
+ }
+
+ @Override
+ public boolean isHidden(@NotNull Project project, AnActionEvent event) {
+ return DebuggerAction.getSelectedNode(event.getDataContext()) == null;
+ }
+
+ @Override
+ public boolean isMarked(@NotNull Project project, @NotNull AnActionEvent event) {
+ final DebuggerTreeNodeImpl node = DebuggerAction.getSelectedNode(event.getDataContext());
+ if (node == null) return false;
+
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (!(descriptor instanceof ValueDescriptor)) return false;
+
+ DebugProcess debugProcess = node.getTree().getDebuggerContext().getDebugProcess();
+ return ((ValueDescriptor)descriptor).getMarkup(debugProcess) != null;
+ }
+
+ public static Color getAutoMarkupColor() {
+ final EditorColorsManager manager = EditorColorsManager.getInstance();
+ final TextAttributes textAttributes = manager.getGlobalScheme().getAttributes(HighlightInfoType.STATIC_FIELD.getAttributesKey());
+ return textAttributes.getForegroundColor();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java
new file mode 100644
index 0000000..c2d204f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000-2011 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.lang.java.JavaLanguage;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.util.containers.OrderedSet;
+import com.intellij.util.text.CharArrayUtil;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * User: Alexander Podkhalyuzin
+ * Date: 22.11.11
+ */
+public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
+ @Override
+ public boolean isAvailable(final SourcePosition position) {
+ final PsiFile file = position.getFile();
+ return file.getLanguage().isKindOf(JavaLanguage.INSTANCE);
+ }
+
+ @Override
+ public List<PsiMethod> findReferencedMethods(final SourcePosition position) {
+ final int line = position.getLine();
+ if (line < 0) {
+ return Collections.emptyList(); // the document has been changed
+ }
+
+ final PsiFile file = position.getFile();
+ final VirtualFile vFile = file.getVirtualFile();
+ if (vFile == null) {
+ // the file is not physical
+ return Collections.emptyList();
+ }
+
+ final Document doc = FileDocumentManager.getInstance().getDocument(vFile);
+ if (doc == null) return Collections.emptyList();
+ if (line >= doc.getLineCount()) {
+ return Collections.emptyList(); // the document has been changed
+ }
+ final int startOffset = doc.getLineStartOffset(line);
+ final TextRange lineRange = new TextRange(startOffset, doc.getLineEndOffset(line));
+ final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), startOffset, " \t");
+ PsiElement element = file.findElementAt(offset);
+ if (element != null && !(element instanceof PsiCompiledElement)) {
+ do {
+ final PsiElement parent = element.getParent();
+ if (parent == null || (parent.getTextOffset() < lineRange.getStartOffset())) {
+ break;
+ }
+ element = parent;
+ }
+ while(true);
+
+ //noinspection unchecked
+ final List<PsiMethod> methods = new OrderedSet<PsiMethod>();
+ final PsiElementVisitor methodCollector = new JavaRecursiveElementWalkingVisitor() {
+ @Override public void visitAnonymousClass(PsiAnonymousClass aClass) { /*skip annonymous classes*/ }
+
+ @Override public void visitStatement(PsiStatement statement) {
+ if (lineRange.intersects(statement.getTextRange())) {
+ super.visitStatement(statement);
+ }
+ }
+
+ @Override public void visitCallExpression(final PsiCallExpression expression) {
+ final PsiMethod psiMethod = expression.resolveMethod();
+ if (psiMethod != null) {
+ methods.add(psiMethod);
+ }
+ super.visitCallExpression(expression);
+ }
+ };
+ element.accept(methodCollector);
+ for (PsiElement sibling = element.getNextSibling(); sibling != null; sibling = sibling.getNextSibling()) {
+ if (!lineRange.intersects(sibling.getTextRange())) {
+ break;
+ }
+ sibling.accept(methodCollector);
+ }
+ return methods;
+ }
+ return Collections.emptyList();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JumpToObjectAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/JumpToObjectAction.java
new file mode 100644
index 0000000..89e32bc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/JumpToObjectAction.java
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiClass;
+import com.sun.jdi.*;
+
+import java.util.List;
+
+public class JumpToObjectAction extends DebuggerAction{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.actions.JumpToObjectAction");
+ public void actionPerformed(AnActionEvent e) {
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(selectedNode == null) {
+ return;
+ }
+
+ final NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ if(!(descriptor instanceof ValueDescriptor)) {
+ return;
+ }
+
+ DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+ if(debugProcess == null) {
+ return;
+ }
+
+ debugProcess.getManagerThread().schedule(new NavigateCommand(debuggerContext, (ValueDescriptor)descriptor, debugProcess, e));
+ }
+
+ public void update(final AnActionEvent e) {
+ if(!isFirstStart(e)) {
+ return;
+ }
+
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+ if(debugProcess == null) {
+ e.getPresentation().setVisible(false);
+ return;
+ }
+
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(selectedNode == null) {
+ e.getPresentation().setVisible(false);
+ return;
+ }
+
+ final NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ if (descriptor instanceof ValueDescriptor) {
+ debugProcess.getManagerThread().schedule(new EnableCommand(debuggerContext, (ValueDescriptor)descriptor, debugProcess, e));
+ }
+ else {
+ e.getPresentation().setVisible(false);
+ }
+ }
+
+ private SourcePosition calcPosition(final ValueDescriptor descriptor, final DebugProcessImpl debugProcess) throws ClassNotLoadedException {
+ final Value value = descriptor.getValue();
+ if(value == null) {
+ return null;
+ }
+
+ Type type = value.type();
+ if(type == null) {
+ return null;
+ }
+
+ try {
+ if(type instanceof ArrayType) {
+ type = ((ArrayType)type).componentType();
+ }
+ if(type instanceof ClassType) {
+ final ClassType clsType = (ClassType)type;
+ final List<Location> locations = clsType.allLineLocations();
+ if(locations.size() > 0) {
+ final Location location = locations.get(0);
+ return ApplicationManager.getApplication().runReadAction(new Computable<SourcePosition>() {
+ public SourcePosition compute() {
+ SourcePosition position = debugProcess.getPositionManager().getSourcePosition(location);
+ // adjust position for non-anonymous classes
+ if (clsType.name().indexOf("$") < 0) {
+ final PsiClass classAt = position != null? JVMNameUtil.getClassAt(position) : null;
+ if (classAt != null) {
+ final SourcePosition classPosition = SourcePosition.createFromElement(classAt);
+ if (classPosition != null) {
+ position = classPosition;
+ }
+ }
+ }
+ return position;
+ }
+ });
+ }
+ }
+ }
+ catch (ClassNotPreparedException e) {
+ LOG.debug(e);
+ }
+ catch (AbsentInformationException e) {
+ LOG.debug(e);
+ }
+ return null;
+ }
+
+ private class NavigateCommand extends SourcePositionCommand {
+ public NavigateCommand(final DebuggerContextImpl debuggerContext, final ValueDescriptor descriptor, final DebugProcessImpl debugProcess, final AnActionEvent e) {
+ super(debuggerContext, descriptor, debugProcess, e);
+ }
+ protected NavigateCommand createRetryCommand() {
+ return new NavigateCommand(myDebuggerContext, myDescriptor, myDebugProcess, myActionEvent);
+ }
+ protected void doAction(final SourcePosition sourcePosition) {
+ if (sourcePosition != null) {
+ sourcePosition.navigate(true);
+ }
+ }
+ }
+
+ private class EnableCommand extends SourcePositionCommand {
+ public EnableCommand(final DebuggerContextImpl debuggerContext, final ValueDescriptor descriptor, final DebugProcessImpl debugProcess, final AnActionEvent e) {
+ super(debuggerContext, descriptor, debugProcess, e);
+ }
+ protected EnableCommand createRetryCommand() {
+ return new EnableCommand(myDebuggerContext, myDescriptor, myDebugProcess, myActionEvent);
+ }
+ protected void doAction(final SourcePosition sourcePosition) {
+ enableAction(myActionEvent, sourcePosition != null);
+ }
+ }
+
+ public abstract class SourcePositionCommand extends SuspendContextCommandImpl {
+ protected final DebuggerContextImpl myDebuggerContext;
+ protected final ValueDescriptor myDescriptor;
+ protected final DebugProcessImpl myDebugProcess;
+ protected final AnActionEvent myActionEvent;
+
+ public SourcePositionCommand(final DebuggerContextImpl debuggerContext,
+ final ValueDescriptor descriptor,
+ final DebugProcessImpl debugProcess,
+ final AnActionEvent actionEvent) {
+ super(debuggerContext.getSuspendContext());
+ myDebuggerContext = debuggerContext;
+ myDescriptor = descriptor;
+ myDebugProcess = debugProcess;
+ myActionEvent = actionEvent;
+ }
+
+ public final void contextAction() throws Exception {
+ try {
+ doAction(calcPosition(myDescriptor, myDebugProcess));
+ }
+ catch (ClassNotLoadedException ex) {
+ final String className = ex.className();
+ if (loadClass(className) != null) {
+ myDebugProcess.getManagerThread().schedule(createRetryCommand());
+ }
+ }
+ }
+
+ protected abstract SourcePositionCommand createRetryCommand();
+
+ protected abstract void doAction(SourcePosition sourcePosition);
+
+ private ReferenceType loadClass(final String className) {
+ final EvaluationContextImpl eContext = myDebuggerContext.createEvaluationContext();
+ try {
+ return myDebugProcess.loadClass(eContext, className, eContext.getClassLoader());
+ }
+ catch(Throwable ignored) {
+ }
+ return null;
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java
new file mode 100644
index 0000000..512ab13
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.RequestHint;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.TextEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListPopup;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.util.containers.OrderedSet;
+import com.intellij.util.text.CharArrayUtil;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
+import gnu.trove.TObjectHashingStrategy;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+public class JvmSmartStepIntoActionHandler extends DebuggerActionHandler {
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ final DebuggerContextImpl debuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ doStep(project, debuggerContext.getSourcePosition(), debuggerContext.getDebuggerSession());
+ }
+
+ private static void doStep(final @NotNull Project project, final @Nullable SourcePosition position, final @NotNull DebuggerSession session) {
+ final VirtualFile file = position != null ? position.getFile().getVirtualFile() : null;
+ final FileEditor fileEditor = file != null? FileEditorManager.getInstance(project).getSelectedEditor(file) : null;
+ if (fileEditor instanceof TextEditor) {
+ for (JvmSmartStepIntoHandler handler : Extensions.getExtensions(JvmSmartStepIntoHandler.EP_NAME)) {
+ if (handler.isAvailable(position) && handler.doSmartStep(position, session, (TextEditor)fileEditor)) return;
+ }
+ }
+ session.stepInto(true, null);
+ }
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ final DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ DebuggerSession debuggerSession = context.getDebuggerSession();
+ final boolean isPaused = debuggerSession != null && debuggerSession.isPaused();
+ final SuspendContextImpl suspendContext = context.getSuspendContext();
+ final boolean hasCurrentThread = suspendContext != null && suspendContext.getThread() != null;
+ return isPaused && hasCurrentThread;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java
new file mode 100644
index 0000000..d02029d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2000-2011 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.RequestHint;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.fileEditor.TextEditor;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListPopup;
+import com.intellij.psi.PsiMethod;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * User: Alexander Podkhalyuzin
+ * Date: 22.11.11
+ */
+public abstract class JvmSmartStepIntoHandler {
+ public static ExtensionPointName<JvmSmartStepIntoHandler> EP_NAME = ExtensionPointName.create("com.intellij.debugger.jvmSmartStepIntoHandler");
+
+ public abstract List<PsiMethod> findReferencedMethods(SourcePosition position);
+
+ public abstract boolean isAvailable(SourcePosition position);
+
+ /**
+ * Override this if you haven't PsiMethod, like in Kotlin.
+ * @param position
+ * @param session
+ * @param fileEditor
+ * @return false to continue for another handler or for default action (step into)
+ */
+ public boolean doSmartStep(SourcePosition position, final DebuggerSession session, TextEditor fileEditor) {
+ final List<PsiMethod> methods = findReferencedMethods(position);
+ if (methods.size() > 0) {
+ if (methods.size() == 1) {
+ session.stepInto(true, getSmartStepFilter(methods.get(0)));
+ }
+ else {
+ final PsiMethodListPopupStep popupStep = new PsiMethodListPopupStep(methods, new PsiMethodListPopupStep.OnChooseRunnable() {
+ public void execute(PsiMethod chosenMethod) {
+ session.stepInto(true, getSmartStepFilter(chosenMethod));
+ }
+ });
+ final ListPopup popup = JBPopupFactory.getInstance().createListPopup(popupStep);
+ final RelativePoint point = DebuggerUIUtil.calcPopupLocation(((TextEditor)fileEditor).getEditor(), position.getLine());
+ popup.show(point);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Override in case if your JVMNames slightly different then it can be provided by getJvmSignature method.
+ * @param method
+ * @return SmartStepFilter
+ */
+ protected RequestHint.SmartStepFilter getSmartStepFilter(PsiMethod method) {
+ return new RequestHint.SmartStepFilter(method);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/MuteBreakpointsActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/MuteBreakpointsActionHandler.java
new file mode 100644
index 0000000..f42c06b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/MuteBreakpointsActionHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.actions.DebuggerToggleActionHandler;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Mar 15, 2005
+ */
+public class MuteBreakpointsActionHandler extends DebuggerToggleActionHandler {
+ public boolean isSelected(@NotNull final Project project, final AnActionEvent event) {
+ DebuggerContextImpl context = DebuggerAction.getDebuggerContext(event.getDataContext());
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+ return debugProcess != null && debugProcess.areBreakpointsMuted();
+ }
+
+ public void setSelected(@NotNull final Project project, final AnActionEvent event, final boolean state) {
+ DebuggerContextImpl context = DebuggerAction.getDebuggerContext(event.getDataContext());
+ final DebugProcessImpl debugProcess = context.getDebugProcess();
+ if(debugProcess != null) {
+ debugProcess.setBreakpointsMuted(state);
+ }
+ }
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ DebuggerContextImpl context = DebuggerAction.getDebuggerContext(event.getDataContext());
+ final DebugProcessImpl debugProcess = context.getDebugProcess();
+ return debugProcess != null;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/NewWatchAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/NewWatchAction.java
new file mode 100644
index 0000000..b7289f5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/NewWatchAction.java
@@ -0,0 +1,39 @@
+/*
+ * 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 NewWatchAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.debugger.ui.impl.MainWatchPanel;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+
+public class NewWatchAction extends DebuggerAction {
+ public void actionPerformed(final AnActionEvent e) {
+ Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ if(project == null) return;
+
+ final MainWatchPanel watchPanel = DebuggerPanelsManager.getInstance(project).getWatchPanel();
+ if (watchPanel != null) {
+ watchPanel.newWatch();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.form b/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.form
new file mode 100644
index 0000000..a0b7a67
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.form
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.actions.ObjectMarkupPropertiesDialog">
+ <grid id="27dc6" binding="myAdditionalPropertiesPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="5" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="372" height="96"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="1dc00" class="com.intellij.openapi.ui.ex.MultiLineLabel" binding="myDescriptionLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="d6e49" class="javax.swing.JCheckBox" binding="myCbMarkAdditionalFields">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Mark values referenced from constant fields"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java b/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java
new file mode 100644
index 0000000..cd4aa14
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.ui.ex.MultiLineLabel;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkerPresentationDialogBase;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 4, 2007
+ */
+public class ObjectMarkupPropertiesDialog extends ValueMarkerPresentationDialogBase {
+ @NonNls private static final String MARK_ALL_REFERENCED_VALUES_KEY = "debugger.mark.all.referenced.values";
+ private JCheckBox myCbMarkAdditionalFields;
+ private final boolean mySuggestAdditionalMarkup;
+ private JPanel myAdditionalPropertiesPanel;
+ private MultiLineLabel myDescriptionLabel;
+
+ public ObjectMarkupPropertiesDialog(@NotNull final String defaultText, boolean suggestAdditionalMarkup) {
+ super(defaultText);
+ mySuggestAdditionalMarkup = suggestAdditionalMarkup;
+ myDescriptionLabel.setText("If the value is referenced by a constant field of an abstract class,\n" +
+ "IDEA could additionally mark all values referenced from this class with the names of referencing fields.");
+ myCbMarkAdditionalFields.setSelected(PropertiesComponent.getInstance().getBoolean(MARK_ALL_REFERENCED_VALUES_KEY, true));
+ init();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (mySuggestAdditionalMarkup) {
+ PropertiesComponent.getInstance().setValue(MARK_ALL_REFERENCED_VALUES_KEY, Boolean.toString(myCbMarkAdditionalFields.isSelected()));
+ }
+ super.doOKAction();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ JComponent mainPanel = super.createCenterPanel();
+ if (!mySuggestAdditionalMarkup) {
+ return mainPanel;
+ }
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(BorderLayout.CENTER, mainPanel);
+ panel.add(BorderLayout.SOUTH, myAdditionalPropertiesPanel);
+ return panel;
+ }
+
+ public boolean isMarkAdditionalFields() {
+ return mySuggestAdditionalMarkup && myCbMarkAdditionalFields.isSelected();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/PauseActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/PauseActionHandler.java
new file mode 100644
index 0000000..7f55f62
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/PauseActionHandler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import com.sun.jdi.request.EventRequest;
+import org.jetbrains.annotations.NotNull;
+
+public class PauseActionHandler extends DebuggerActionHandler {
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession().pause();
+ }
+
+ @Override
+ public boolean isHidden(@NotNull Project project, AnActionEvent event) {
+ return DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession() == null;
+ }
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ final DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession();
+ return debuggerSession != null && !debuggerSession.getProcess().isPausePressed() &&
+ (debuggerSession.isEvaluating() ||
+ debuggerSession.isRunning() || isSingleThreadSuspended(debuggerSession)
+ );
+ }
+
+ private static boolean isSingleThreadSuspended(final DebuggerSession debuggerSession) {
+ final SuspendContextImpl suspendContext = debuggerSession.getContextManager().getContext().getSuspendContext();
+ return suspendContext != null && !suspendContext.isResumed() && suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/PlaceInDocument.java b/java/debugger/impl/src/com/intellij/debugger/actions/PlaceInDocument.java
new file mode 100644
index 0000000..808f226
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/PlaceInDocument.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.openapi.editor.Document;
+
+/**
+ * User: lex
+ * Date: Oct 7, 2003
+ * Time: 3:12:54 PM
+ */
+interface PlaceInDocument {
+ public Document getDocument();
+ public int getOffset();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java
new file mode 100644
index 0000000..f2f310c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.sun.jdi.InvalidStackFrameException;
+import com.sun.jdi.NativeMethodException;
+import com.sun.jdi.VMDisconnectedException;
+import org.jetbrains.annotations.Nullable;
+
+public class PopFrameAction extends DebuggerAction {
+ public void actionPerformed(AnActionEvent e) {
+ Project project = e.getData(PlatformDataKeys.PROJECT);
+ StackFrameProxyImpl stackFrame = getStackFrameProxy(e);
+ if(stackFrame == null) {
+ return;
+ }
+ try {
+ DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
+ DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+ if(debugProcess == null) {
+ return;
+ }
+ debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame));
+ }
+ catch (NativeMethodException e2){
+ Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
+ }
+ catch (InvalidStackFrameException ignored) {
+ }
+ catch(VMDisconnectedException vde) {
+ }
+ }
+
+ @Nullable
+ private static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) {
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(selectedNode != null) {
+ NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
+ if(descriptor instanceof StackFrameDescriptorImpl) {
+ if(selectedNode.getNextSibling() != null) {
+ StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor);
+ return frameDescriptor.getFrameProxy();
+ }
+ return null;
+ }
+ else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) {
+ return null;
+ }
+ }
+ DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
+ StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy();
+
+ if(frameProxy == null || frameProxy.isBottom()) {
+ return null;
+ }
+ return frameProxy;
+ }
+
+ private static boolean isAtBreakpoint(AnActionEvent e) {
+ DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
+ if(selectedNode != null && selectedNode.getDescriptor() instanceof StackFrameDescriptorImpl) {
+ DebuggerTreeNodeImpl parent = selectedNode.getParent();
+ if(parent != null) {
+ return ((ThreadDescriptorImpl)parent.getDescriptor()).isAtBreakpoint();
+ }
+ }
+ DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
+ SuspendContextImpl suspendContext = debuggerContext.getSuspendContext();
+ return suspendContext != null && debuggerContext.getThreadProxy() == suspendContext.getThread();
+ }
+
+ public void update(AnActionEvent e) {
+ boolean enable = false;
+ StackFrameProxyImpl stackFrameProxy = getStackFrameProxy(e);
+
+ if(stackFrameProxy != null && isAtBreakpoint(e)) {
+ VirtualMachineProxyImpl virtualMachineProxy = stackFrameProxy.getVirtualMachine();
+ enable = virtualMachineProxy.canPopFrames();
+ }
+
+ if(ActionPlaces.MAIN_MENU.equals(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) {
+ e.getPresentation().setEnabled(enable);
+ }
+ else {
+ e.getPresentation().setVisible(enable);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/PsiMethodListPopupStep.java b/java/debugger/impl/src/com/intellij/debugger/actions/PsiMethodListPopupStep.java
new file mode 100644
index 0000000..7a6fde9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/PsiMethodListPopupStep.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.ui.popup.*;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiSubstitutor;
+import com.intellij.psi.util.PsiFormatUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+* Date: Nov 21, 2006
+*/
+class PsiMethodListPopupStep implements ListPopupStep {
+ private final List<PsiMethod> myMethods;
+ private final OnChooseRunnable myStepRunnable;
+
+
+ public static interface OnChooseRunnable {
+ void execute(PsiMethod chosenMethod);
+ }
+
+ public PsiMethodListPopupStep(final List<PsiMethod> methods, final OnChooseRunnable stepRunnable) {
+ myMethods = methods;
+ myStepRunnable = stepRunnable;
+ }
+
+ @NotNull
+ public List getValues() {
+ return myMethods;
+ }
+
+ public boolean isSelectable(Object value) {
+ return true;
+ }
+
+ public Icon getIconFor(Object aValue) {
+ if (aValue instanceof PsiMethod) {
+ PsiMethod method = (PsiMethod)aValue;
+ return method.getIcon(0);
+ }
+ return null;
+ }
+
+ @NotNull
+ public String getTextFor(Object value) {
+ if (value instanceof PsiMethod) {
+ return PsiFormatUtil.formatMethod(
+ (PsiMethod)value,
+ PsiSubstitutor.EMPTY,
+ PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS,
+ PsiFormatUtil.SHOW_TYPE,
+ 999
+ );
+ }
+ return value.toString();
+ }
+
+ public ListSeparator getSeparatorAbove(Object value) {
+ return null;
+ }
+
+ public int getDefaultOptionIndex() {
+ return 0;
+ }
+
+ public String getTitle() {
+ return DebuggerBundle.message("title.smart.step.popup");
+ }
+
+ public PopupStep onChosen(Object selectedValue, final boolean finalChoice) {
+ if (finalChoice) {
+ myStepRunnable.execute((PsiMethod)selectedValue);
+ }
+ return FINAL_CHOICE;
+ }
+
+ public Runnable getFinalRunnable() {
+ return null;
+ }
+
+ public boolean hasSubstep(Object selectedValue) {
+ return false;
+ }
+
+ public void canceled() {
+ }
+
+ public boolean isMnemonicsNavigationEnabled() {
+ return false;
+ }
+
+ public MnemonicNavigationFilter getMnemonicNavigationFilter() {
+ return null;
+ }
+
+ public boolean isSpeedSearchEnabled() {
+ return false;
+ }
+
+ public SpeedSearchFilter getSpeedSearchFilter() {
+ return null;
+ }
+
+ public boolean isAutoSelectionEnabled() {
+ return false;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/QuickEvaluateActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/QuickEvaluateActionHandler.java
new file mode 100644
index 0000000..d67b2bf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/QuickEvaluateActionHandler.java
@@ -0,0 +1,55 @@
+/*
+ * 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 EvaluateAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.ValueHint;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.evaluate.quick.common.QuickEvaluateHandler;
+import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint;
+import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+public class QuickEvaluateActionHandler extends QuickEvaluateHandler {
+
+ public boolean isEnabled(@NotNull final Project project) {
+ DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession();
+ return debuggerSession != null && debuggerSession.isPaused();
+ }
+
+ public AbstractValueHint createValueHint(@NotNull final Project project, @NotNull final Editor editor, @NotNull final Point point, final ValueHintType type) {
+ return ValueHint.createValueHint(project, editor, point, type);
+ }
+
+ public boolean canShowHint(@NotNull final Project project) {
+ DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession();
+ return debuggerSession != null && debuggerSession.isAttached();
+ }
+
+ public int getValueLookupDelay(final Project project) {
+ return DebuggerSettings.getInstance().VALUE_LOOKUP_DELAY;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/RemoveAllWatchesAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/RemoveAllWatchesAction.java
new file mode 100644
index 0000000..e677322
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/RemoveAllWatchesAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+
+import java.util.Enumeration;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 6:24:44 PM
+ */
+public class RemoveAllWatchesAction extends RemoveWatchAction {
+ protected DebuggerTreeNodeImpl[] getNodesToDelete(AnActionEvent e) {
+ DebuggerTree tree = getTree(e.getDataContext());
+ if(tree == null) return null;
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)tree.getModel().getRoot();
+ DebuggerTreeNodeImpl [] result = new DebuggerTreeNodeImpl[root.getChildCount()];
+ int i = 0;
+ for(Enumeration enumeration = root.children(); enumeration.hasMoreElements(); i++) {
+ DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)enumeration.nextElement();
+ result[i] = node;
+ }
+ return result;
+ }
+
+ protected void updatePresentation(Presentation presentation, int watchesCount) {
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/RemoveWatchAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/RemoveWatchAction.java
new file mode 100644
index 0000000..3222419
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/RemoveWatchAction.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.ui.impl.MainWatchPanel;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+
+import java.util.ArrayList;
+
+public class RemoveWatchAction extends DebuggerAction {
+ protected DebuggerTreeNodeImpl[] getNodesToDelete(AnActionEvent e) {
+ DebuggerTreeNodeImpl[] selectedNodes = getSelectedNodes(e.getDataContext());
+ if(selectedNodes == null) return null;
+ ArrayList<DebuggerTreeNodeImpl> selectedWatches = new ArrayList<DebuggerTreeNodeImpl>();
+ for (int i = 0; i < selectedNodes.length; i++) {
+ if(selectedNodes[i].getDescriptor() instanceof WatchItemDescriptor) {
+ selectedWatches.add(selectedNodes[i]);
+ }
+ }
+
+ return selectedWatches.toArray(new DebuggerTreeNodeImpl[selectedWatches.size()]);
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ DebuggerTreeNodeImpl [] nodes = getNodesToDelete(e);
+ if (nodes == null || nodes.length == 0) return;
+
+ MainWatchPanel watchPanel = (MainWatchPanel)getPanel(e.getDataContext());
+
+ for (int i = 0; i < nodes.length; i++) {
+ DebuggerTreeNodeImpl node = nodes[i];
+ watchPanel.getWatchTree().removeWatch(node);
+ }
+ }
+
+ protected void updatePresentation(Presentation presentation, int watchesCount) {
+ presentation.setText(DebuggerBundle.message("action.remove.watch.text", watchesCount));
+ }
+
+ public void update(AnActionEvent event) {
+ Presentation presentation = event.getPresentation();
+ DebuggerTreeNodeImpl[] nodes = getNodesToDelete(event);
+ if (nodes != null && nodes.length > 0) {
+ presentation.setEnabled(true);
+ }
+ else {
+ presentation.setEnabled(false);
+ }
+ updatePresentation(presentation, nodes != null? nodes.length : 0);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ResumeActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/ResumeActionHandler.java
new file mode 100644
index 0000000..0a975bf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ResumeActionHandler.java
@@ -0,0 +1,35 @@
+
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import org.jetbrains.annotations.NotNull;
+
+public class ResumeActionHandler extends DebuggerActionHandler {
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession().resume();
+ }
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ DebuggerSession debuggerSession = (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession();
+ return debuggerSession != null && debuggerSession.isPaused();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java
new file mode 100644
index 0000000..fe3ef56
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.sun.jdi.request.EventRequest;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 7:35:09 PM
+ */
+public class ResumeThreadAction extends DebuggerAction{
+ public void actionPerformed(final AnActionEvent e) {
+ DebuggerTreeNodeImpl[] selectedNode = getSelectedNodes(e.getDataContext());
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext());
+ final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+
+ //noinspection ConstantConditions
+ for (final DebuggerTreeNodeImpl debuggerTreeNode : selectedNode) {
+ final ThreadDescriptorImpl threadDescriptor = ((ThreadDescriptorImpl)debuggerTreeNode.getDescriptor());
+
+ if (threadDescriptor.isSuspended()) {
+ final ThreadReferenceProxyImpl thread = threadDescriptor.getThreadReference();
+ debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
+ public void contextAction() throws Exception {
+ debugProcess.createResumeThreadCommand(getSuspendContext(), thread).run();
+ debuggerTreeNode.calcValue();
+ }
+ });
+ }
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ DebuggerTreeNodeImpl[] selectedNodes = getSelectedNodes(e.getDataContext());
+
+ boolean visible = false;
+ boolean enabled = false;
+ String text = DebuggerBundle.message("action.resume.thread.text.resume");
+
+ if(selectedNodes != null && selectedNodes.length > 0){
+ visible = true;
+ enabled = true;
+ for (DebuggerTreeNodeImpl selectedNode : selectedNodes) {
+ final NodeDescriptorImpl threadDescriptor = selectedNode.getDescriptor();
+ if (!(threadDescriptor instanceof ThreadDescriptorImpl) || !((ThreadDescriptorImpl)threadDescriptor).isSuspended()) {
+ visible = false;
+ break;
+ }
+ }
+ if (visible) {
+ for (DebuggerTreeNodeImpl selectedNode : selectedNodes) {
+ final ThreadDescriptorImpl threadDescriptor = (ThreadDescriptorImpl)selectedNode.getDescriptor();
+ if (threadDescriptor.getSuspendContext().getSuspendPolicy() == EventRequest.SUSPEND_ALL && !threadDescriptor.isFrozen()) {
+ enabled = false;
+ break;
+ }
+ else {
+ if (threadDescriptor.isFrozen()) {
+ text = DebuggerBundle.message("action.resume.thread.text.unfreeze");
+ }
+ }
+ }
+ }
+ }
+ final Presentation presentation = e.getPresentation();
+ presentation.setText(text);
+ presentation.setVisible(visible);
+ presentation.setEnabled(enabled);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/RunToCursorActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/RunToCursorActionHandler.java
new file mode 100644
index 0000000..56ed1c5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/RunToCursorActionHandler.java
@@ -0,0 +1,88 @@
+/*
+ * 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 RunToCursorAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import org.jetbrains.annotations.NotNull;
+
+public class RunToCursorActionHandler extends DebuggerActionHandler {
+ private final boolean myIgnoreBreakpoints;
+
+ public RunToCursorActionHandler() {
+ this(false);
+ }
+
+ protected RunToCursorActionHandler(boolean ignoreBreakpoints) {
+ myIgnoreBreakpoints = ignoreBreakpoints;
+ }
+
+ public boolean isEnabled(final @NotNull Project project, final AnActionEvent event) {
+
+ Editor editor = event.getData(PlatformDataKeys.EDITOR);
+
+ if (editor == null) {
+ return false;
+ }
+
+ PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+ FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+ if (file == null) {
+ return false;
+ }
+
+ final VirtualFile virtualFile = file.getVirtualFile();
+ FileType fileType = virtualFile != null ? virtualFile.getFileType() : null;
+ if (DebuggerUtils.supportsJVMDebugging(fileType) || DebuggerUtils.supportsJVMDebugging(file)) {
+ DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession();
+ return debuggerSession != null && debuggerSession.isPaused();
+ }
+
+ return false;
+ }
+
+
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ Editor editor = event.getData(PlatformDataKeys.EDITOR);
+ if (editor == null) {
+ return;
+ }
+ DebuggerContextImpl context = DebuggerManagerEx.getInstanceEx(project).getContext();
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+ if (debugProcess == null) {
+ return;
+ }
+ context.getDebuggerSession().runToCursor(editor.getDocument(), editor.getCaretModel().getLogicalPosition().line, myIgnoreBreakpoints);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java
new file mode 100644
index 0000000..9fd1825
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.*;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.*;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.debugger.ui.EditorEvaluationCommand;
+import com.intellij.debugger.ui.impl.DebuggerTreeRenderer;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.debugger.ui.tree.render.HexRenderer;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.debugger.ui.tree.render.ValueLabelRenderer;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter;
+import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.ui.SimpleColoredComponent;
+import com.intellij.util.IJSwingUtilities;
+import com.sun.jdi.*;
+
+import javax.swing.*;
+
+/*
+ * Class SetValueAction
+ * @author Jeka
+ */
+public class SetValueAction extends DebuggerAction {
+ public void update(AnActionEvent e) {
+ boolean enable = false;
+ DebuggerTreeNodeImpl node = getSelectedNode(e.getDataContext());
+ if (node != null) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if(descriptor instanceof ValueDescriptorImpl){
+ ValueDescriptorImpl valueDescriptor = ((ValueDescriptorImpl)descriptor);
+ enable = valueDescriptor.canSetValue();
+ }
+ }
+ e.getPresentation().setVisible(enable);
+ }
+
+ private void update(final DebuggerContextImpl context) {
+ DebuggerInvocationUtil.swingInvokeLater(context.getProject(), new Runnable() {
+ public void run() {
+ context.getDebuggerSession().refresh(false);
+ }
+ });
+ //node.setState(context);
+ }
+
+ public void actionPerformed(final AnActionEvent event) {
+ final DebuggerTreeNodeImpl node = getSelectedNode(event.getDataContext());
+ if (node == null) {
+ return;
+ }
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (!(descriptor instanceof ValueDescriptorImpl)) {
+ return;
+ }
+ if(!((ValueDescriptorImpl)descriptor).canSetValue()) {
+ return;
+ }
+
+ final DebuggerTree tree = getTree(event.getDataContext());
+ final DebuggerContextImpl debuggerContext = getDebuggerContext(event.getDataContext());
+ tree.saveState(node);
+
+ if (descriptor instanceof FieldDescriptorImpl) {
+ FieldDescriptorImpl fieldDescriptor = (FieldDescriptorImpl)descriptor;
+ final Field field = fieldDescriptor.getField();
+ if (!field.isStatic()) {
+ final ObjectReference object = fieldDescriptor.getObject();
+ if (object != null) {
+ askAndSet(node, debuggerContext, new SetValueRunnable() {
+ public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
+ object.setValue(field, preprocessValue(evaluationContext, newValue, field.type()));
+ update(debuggerContext);
+ }
+
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException,
+ EvaluateException {
+ return evaluationContext.getDebugProcess().loadClass(evaluationContext, className, field.declaringType().classLoader());
+ }
+ });
+ }
+ }
+ else {
+ // field is static
+ ReferenceType refType = field.declaringType();
+ if (refType instanceof ClassType) {
+ final ClassType classType = (ClassType)refType;
+ askAndSet(node, debuggerContext, new SetValueRunnable() {
+ public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
+ classType.setValue(field, preprocessValue(evaluationContext, newValue, field.type()));
+ update(debuggerContext);
+ }
+
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException,
+ EvaluateException {
+ return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
+ field.declaringType().classLoader());
+ }
+ });
+ }
+ }
+ }
+ else if (descriptor instanceof LocalVariableDescriptorImpl) {
+ LocalVariableDescriptorImpl localDescriptor = (LocalVariableDescriptorImpl)descriptor;
+ final LocalVariableProxyImpl local = localDescriptor.getLocalVariable();
+ if (local != null) {
+ askAndSet(node, debuggerContext, new SetValueRunnable() {
+ public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException,
+ InvalidTypeException,
+ EvaluateException {
+ debuggerContext.getFrameProxy().setValue(local, preprocessValue(evaluationContext, newValue, local.getType()));
+ update(debuggerContext);
+ }
+
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException,
+ EvaluateException {
+ return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
+ evaluationContext.getClassLoader());
+ }
+ });
+ }
+ }
+ else if (descriptor instanceof ArrayElementDescriptorImpl) {
+ final ArrayElementDescriptorImpl elementDescriptor = (ArrayElementDescriptorImpl)descriptor;
+ final ArrayReference array = elementDescriptor.getArray();
+ if (array != null) {
+ if (VirtualMachineProxyImpl.isCollected(array)) {
+ // will only be the case if debugger does not use ObjectReference.disableCollection() because of Patches.IBM_JDK_DISABLE_COLLECTION_BUG
+ Messages.showWarningDialog(tree, DebuggerBundle.message("evaluation.error.array.collected") + "\n"+ DebuggerBundle.message("warning.recalculate"), DebuggerBundle.message("title.set.value"));
+ node.getParent().calcValue();
+ return;
+ }
+ final ArrayType arrType = (ArrayType)array.referenceType();
+ askAndSet(node, debuggerContext, new SetValueRunnable() {
+ public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
+ array.setValue(elementDescriptor.getIndex(), preprocessValue(evaluationContext, newValue, arrType.componentType()));
+ update(debuggerContext);
+ }
+
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException,
+ EvaluateException {
+ return evaluationContext.getDebugProcess().loadClass(evaluationContext, className, arrType.classLoader());
+ }
+ });
+ }
+ }
+ else if (descriptor instanceof EvaluationDescriptor) {
+ final EvaluationDescriptor evaluationDescriptor = (EvaluationDescriptor)descriptor;
+ if (evaluationDescriptor.canSetValue()) {
+ askAndSet(node, debuggerContext, new SetValueRunnable() {
+ public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
+ final Modifier modifier = evaluationDescriptor.getModifier();
+ modifier.setValue(preprocessValue(evaluationContext, newValue, modifier.getExpectedType()));
+ update(debuggerContext);
+ }
+
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException,
+ EvaluateException {
+ return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
+ evaluationContext.getClassLoader());
+ }
+ });
+ }
+ }
+ }
+
+ private Value preprocessValue(EvaluationContextImpl context, Value value, Type varType) throws EvaluateException {
+ if (value != null && "java.lang.String".equals(varType.name()) && !(value instanceof StringReference)) {
+ String v = DebuggerUtilsEx.getValueAsString(context, value);
+ if (v != null) {
+ value = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf(v);
+ }
+ }
+ if(value instanceof DoubleValue) {
+ double dValue = ((DoubleValue) value).doubleValue();
+ if(varType instanceof FloatType && Float.MIN_VALUE <= dValue && dValue <= Float.MAX_VALUE){
+ value = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf((float)dValue);
+ }
+ }
+ if (value != null) {
+ if (varType instanceof PrimitiveType) {
+ if (!(value instanceof PrimitiveValue)) {
+ value = (Value)new UnBoxingEvaluator(new IdentityEvaluator(value)).evaluate(context);
+ }
+ }
+ else if (UnBoxingEvaluator.isTypeUnboxable(varType.name())) {
+ // variable is not primitive and boxing/unboxing is applicable
+ if (value instanceof PrimitiveValue) {
+ value = (Value)new BoxingEvaluator(new IdentityEvaluator(value)).evaluate(context);
+ }
+ }
+ }
+ return value;
+ }
+
+ private static interface SetValueRunnable {
+ void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException,
+ InvalidTypeException,
+ EvaluateException,
+ IncompatibleThreadStateException;
+ ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws EvaluateException,
+ InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException;
+ }
+
+ private static void setValue(String expressionToShow, ExpressionEvaluator evaluator, EvaluationContextImpl evaluationContext, SetValueRunnable setValueRunnable) throws EvaluateException {
+ Value value;
+ try {
+ value = evaluator.evaluate(evaluationContext);
+
+ setValueRunnable.setValue(evaluationContext, value);
+ }
+ catch (IllegalArgumentException ex) {
+ throw EvaluateExceptionUtil.createEvaluateException(ex.getMessage());
+ }
+ catch (InvalidTypeException ex) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.type.mismatch"));
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (ClassNotLoadedException ex) {
+ if (!evaluationContext.isAutoLoadClasses()) {
+ throw EvaluateExceptionUtil.createEvaluateException(ex);
+ }
+ final ReferenceType refType;
+ try {
+ refType = setValueRunnable.loadClass(evaluationContext, ex.className());
+ if (refType != null) {
+ //try again
+ setValue(expressionToShow, evaluator, evaluationContext, setValueRunnable);
+ }
+ }
+ catch (InvocationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (ClassNotLoadedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InvalidTypeException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (ObjectCollectedException e) {
+ throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
+ }
+ }
+ }
+
+ private void askAndSet(final DebuggerTreeNodeImpl node, final DebuggerContextImpl debuggerContext, final SetValueRunnable setValueRunnable) {
+ ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, debuggerContext.getProject());
+
+ SuspendContextCommandImpl askSetAction = new DebuggerContextCommandImpl(debuggerContext) {
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+
+ public void threadAction() {
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ String initialString = "";
+ if (descriptor instanceof ValueDescriptorImpl) {
+ Value currentValue = ((ValueDescriptorImpl) descriptor).getValue();
+ if (currentValue instanceof StringReference) {
+ initialString = DebuggerUtilsEx.getValueOrErrorAsString(debuggerContext.createEvaluationContext(), currentValue);
+ initialString = initialString == null ? "" : "\"" + DebuggerUtilsEx.translateStringValue(initialString) + "\"";
+ }
+ else if (currentValue instanceof PrimitiveValue) {
+ ValueLabelRenderer renderer = ((ValueDescriptorImpl) descriptor).getRenderer(debuggerContext.getDebugProcess());
+ initialString = getDisplayableString((PrimitiveValue) currentValue, renderer instanceof NodeRenderer && HexRenderer.UNIQUE_ID.equals(renderer.getUniqueId()));
+ }
+
+ final String initialString1 = initialString;
+ final Project project = debuggerContext.getProject();
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ showEditor(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, initialString1), node, debuggerContext, setValueRunnable);
+ }
+ });
+ }
+ }
+ };
+
+ progressWindow.setTitle(DebuggerBundle.message("title.evaluating"));
+ debuggerContext.getDebugProcess().getManagerThread().startProgress(askSetAction, progressWindow);
+ }
+
+ private void showEditor(final TextWithImports initialString,
+ final DebuggerTreeNodeImpl node,
+ final DebuggerContextImpl debuggerContext,
+ final SetValueRunnable setValueRunnable) {
+ final JPanel editorPanel = new JPanel();
+ editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS));
+ SimpleColoredComponent label = new SimpleColoredComponent();
+ label.setIcon(node.getIcon());
+ DebuggerTreeRenderer.getDescriptorTitle(debuggerContext, node.getDescriptor()).appendToComponent(label);
+ editorPanel.add(label);
+
+ final DebuggerExpressionComboBox comboBox = new DebuggerExpressionComboBox(
+ debuggerContext.getProject(),
+ PositionUtil.getContextElement(debuggerContext),
+ "setValue", DefaultCodeFragmentFactory.getInstance());
+ comboBox.setText(initialString);
+ comboBox.selectAll();
+ editorPanel.add(comboBox);
+
+ final DebuggerTreeInplaceEditor editor = new DebuggerTreeInplaceEditor(node) {
+ public JComponent createInplaceEditorComponent() {
+ return editorPanel;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return comboBox;
+ }
+
+ public Editor getEditor() {
+ return comboBox.getEditor();
+ }
+
+ public JComponent getEditorComponent() {
+ return comboBox.getEditorComponent();
+ }
+
+ private void flushValue() {
+ Editor editor = getEditor();
+ if(editor == null) {
+ return;
+ }
+
+ final TextWithImports text = comboBox.getText();
+
+ PsiFile psiFile = PsiDocumentManager.getInstance(debuggerContext.getProject()).getPsiFile(editor.getDocument());
+
+ final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, getProject());
+ EditorEvaluationCommand evaluationCommand = new EditorEvaluationCommand(getEditor(), psiFile, debuggerContext, progressWindow) {
+ public void threadAction() {
+ try {
+ evaluate();
+ }
+ catch(EvaluateException e) {
+ progressWindow.cancel();
+ }
+ catch(ProcessCanceledException e) {
+ progressWindow.cancel();
+ }
+ finally{
+ if (!progressWindow.isCanceled()) {
+ DebuggerInvocationUtil.swingInvokeLater(debuggerContext.getProject(), new Runnable() {
+ public void run() {
+ comboBox.addRecent(text);
+ cancelEditing();
+ }
+ });
+ }
+ }
+ }
+
+ protected Object evaluate(final EvaluationContextImpl evaluationContext) throws EvaluateException {
+ ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(evaluationContext.getProject(), new com.intellij.debugger.EvaluatingComputable<ExpressionEvaluator>() {
+ public ExpressionEvaluator compute() throws EvaluateException {
+ return EvaluatorBuilderImpl.build(text, ContextUtil.getContextElement(evaluationContext), ContextUtil.getSourcePosition(evaluationContext));
+ }
+ });
+
+ SetValueAction.setValue(text.getText(), evaluator, evaluationContext, new SetValueRunnable() {
+ public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException,
+ InvalidTypeException,
+ EvaluateException,
+ IncompatibleThreadStateException {
+ if(!progressWindow.isCanceled()) {
+ setValueRunnable.setValue(evaluationContext, newValue);
+ node.calcValue();
+ }
+ }
+
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
+ ClassNotLoadedException,
+ EvaluateException,
+ IncompatibleThreadStateException,
+ InvalidTypeException {
+ return setValueRunnable.loadClass(evaluationContext, className);
+ }
+ });
+
+ return null;
+ }
+ };
+
+ progressWindow.addListener(new ProgressIndicatorListenerAdapter() {
+ //should return whether to stop processing
+ public void stopped() {
+ if(!progressWindow.isCanceled()) {
+ IJSwingUtilities.invoke(new Runnable() {
+ public void run() {
+ cancelEditing();
+ }
+ });
+ }
+ }
+
+
+ });
+
+ progressWindow.setTitle(DebuggerBundle.message("progress.set.value"));
+ debuggerContext.getDebugProcess().getManagerThread().startProgress(evaluationCommand, progressWindow);
+ }
+
+ public void cancelEditing() {
+ try {
+ super.cancelEditing();
+ }
+ finally {
+ comboBox.dispose();
+ }
+ }
+
+ public void doOKAction() {
+ try {
+ flushValue();
+ }
+ finally {
+ comboBox.dispose();
+ }
+ }
+
+ };
+
+ final DebuggerStateManager stateManager = DebuggerManagerEx.getInstanceEx(debuggerContext.getProject()).getContextManager();
+
+ stateManager.addListener(new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ stateManager.removeListener(this);
+ editor.cancelEditing();
+ }
+ });
+
+ node.getTree().hideTooltip();
+
+ editor.show();
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static String getDisplayableString(PrimitiveValue value, boolean showAsHex) {
+ if (value instanceof CharValue) {
+ long longValue = value.longValue();
+ return showAsHex ? "0x" + Long.toHexString(longValue).toUpperCase() : Long.toString(longValue);
+ }
+ if (value instanceof ByteValue) {
+ byte val = value.byteValue();
+ String strValue = Integer.toHexString(val).toUpperCase();
+ if (strValue.length() > 2) {
+ strValue = strValue.substring(strValue.length() - 2);
+ }
+ return showAsHex ? "0x" + strValue : value.toString();
+ }
+ if (value instanceof ShortValue) {
+ short val = value.shortValue();
+ String strValue = Integer.toHexString(val).toUpperCase();
+ if (strValue.length() > 4) {
+ strValue = strValue.substring(strValue.length() - 4);
+ }
+ return showAsHex ? "0x" + strValue : value.toString();
+ }
+ if (value instanceof IntegerValue) {
+ int val = value.intValue();
+ return showAsHex ? "0x" + Integer.toHexString(val).toUpperCase() : value.toString();
+ }
+ if (value instanceof LongValue) {
+ long val = value.longValue();
+ return showAsHex ? "0x" + Long.toHexString(val).toUpperCase() + "L" : value.toString() + "L";
+ }
+ return DebuggerUtilsEx.translateStringValue(value.toString());
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ShowExecutionPointActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/ShowExecutionPointActionHandler.java
new file mode 100644
index 0000000..6dc22d3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ShowExecutionPointActionHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import org.jetbrains.annotations.NotNull;
+
+public class ShowExecutionPointActionHandler extends DebuggerActionHandler {
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession().showExecutionPoint();
+ }
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ DebuggerSession debuggerSession = (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession();
+ return debuggerSession != null && debuggerSession.isPaused() &&
+ debuggerSession.getContextManager().getContext().getSuspendContext().getThread() != null;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ShowFrameAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ShowFrameAction.java
new file mode 100644
index 0000000..6b1bf1f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ShowFrameAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataConstants;
+import com.intellij.openapi.project.Project;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 7:45:21 PM
+ */
+public class ShowFrameAction extends GotoFrameSourceAction {
+ public void actionPerformed(AnActionEvent e) {
+ super.actionPerformed(e);
+ DebuggerStateManager stateManager = getContextManager(e.getDataContext());
+ DebuggerContextImpl context = stateManager.getContext();
+
+ if(context != null) {
+ DebuggerPanelsManager.getInstance(context.getProject()).showFramePanel();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/StepIntoActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/StepIntoActionHandler.java
new file mode 100644
index 0000000..e1e07ec
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/StepIntoActionHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class StepIntoActionHandler extends AbstractSteppingActionHandler {
+ public void perform(@NotNull final Project project, AnActionEvent e) {
+ final DebuggerSession session = getSession(project);
+ if (session != null) {
+ session.stepInto(false, null);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/StepOutActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/StepOutActionHandler.java
new file mode 100644
index 0000000..161c649
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/StepOutActionHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class StepOutActionHandler extends AbstractSteppingActionHandler {
+ public void perform(@NotNull final Project project, AnActionEvent e) {
+ final DebuggerSession session = getSession(project);
+ if (session != null) {
+ session.stepOut();
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/StepOverActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/StepOverActionHandler.java
new file mode 100644
index 0000000..bd53245
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/StepOverActionHandler.java
@@ -0,0 +1,31 @@
+
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class StepOverActionHandler extends AbstractSteppingActionHandler {
+ public void perform(@NotNull final Project project, AnActionEvent e) {
+ final DebuggerSession session = getSession(project);
+ if (session != null) {
+ session.stepOver(false);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java
new file mode 100644
index 0000000..a1ddd95
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2000-2010 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 ExportThreadsAction
+ * @author Eugene Zhuravlev
+ * @author Sascha Weinreuter
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.debugger.ui.DebuggerSessionTab;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.project.Project;
+import com.intellij.unscramble.ThreadDumpParser;
+import com.intellij.unscramble.ThreadState;
+import com.intellij.util.SmartList;
+import com.sun.jdi.*;
+import gnu.trove.TIntObjectHashMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ThreadDumpAction extends AnAction implements AnAction.TransparentUpdate {
+
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ if (project == null) {
+ return;
+ }
+ DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+
+ final DebuggerSession session = context.getDebuggerSession();
+ if(session != null && session.isAttached()) {
+ final DebugProcessImpl process = context.getDebugProcess();
+ process.getManagerThread().invoke(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ final VirtualMachineProxyImpl vm = process.getVirtualMachineProxy();
+ vm.suspend();
+ try {
+ final List<ThreadState> threads = buildThreadStates(vm);
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ final DebuggerSessionTab sessionTab = DebuggerPanelsManager.getInstance(project).getSessionTab();
+ if (sessionTab != null) {
+ sessionTab.addThreadDump(threads);
+ }
+ }
+ }, ModalityState.NON_MODAL);
+ }
+ finally {
+ vm.resume();
+ }
+ }
+ });
+ }
+ }
+
+ private static List<ThreadState> buildThreadStates(VirtualMachineProxyImpl vmProxy) {
+ final List<ThreadReference> threads = vmProxy.getVirtualMachine().allThreads();
+ final List<ThreadState> result = new ArrayList<ThreadState>();
+ final Map<String, ThreadState> nameToThreadMap = new HashMap<String, ThreadState>();
+ final Map<String, String> waitingMap = new HashMap<String, String>(); // key 'waits_for' value
+ for (ThreadReference threadReference : threads) {
+ final StringBuilder buffer = new StringBuilder();
+ boolean hasEmptyStack = true;
+ final int threadStatus = threadReference.status();
+ if (threadStatus == ThreadReference.THREAD_STATUS_ZOMBIE) {
+ continue;
+ }
+ final String threadName = threadName(threadReference);
+ final ThreadState threadState = new ThreadState(threadName, threadStatusToState(threadStatus));
+ nameToThreadMap.put(threadName, threadState);
+ result.add(threadState);
+ threadState.setJavaThreadState(threadStatusToJavaThreadState(threadStatus));
+
+ buffer.append("\"").append(threadName).append("\"");
+ ReferenceType referenceType = threadReference.referenceType();
+ if (referenceType != null) {
+ //noinspection HardCodedStringLiteral
+ Field daemon = referenceType.fieldByName("daemon");
+ if (daemon != null) {
+ Value value = threadReference.getValue(daemon);
+ if (value instanceof BooleanValue && ((BooleanValue)value).booleanValue()) {
+ buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.daemon"));
+ threadState.setDaemon(true);
+ }
+ }
+
+ //noinspection HardCodedStringLiteral
+ Field priority = referenceType.fieldByName("priority");
+ if (priority != null) {
+ Value value = threadReference.getValue(priority);
+ if (value instanceof IntegerValue) {
+ buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.priority", ((IntegerValue)value).intValue()));
+ }
+ }
+
+ Field tid = referenceType.fieldByName("tid");
+ if (tid != null) {
+ Value value = threadReference.getValue(tid);
+ if (value instanceof LongValue) {
+ buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.tid", Long.toHexString(((LongValue)value).longValue())));
+ buffer.append(" nid=NA");
+ }
+ }
+ }
+ //ThreadGroupReference groupReference = threadReference.threadGroup();
+ //if (groupReference != null) {
+ // buffer.append(", ").append(DebuggerBundle.message("threads.export.attribute.label.group", groupReference.name()));
+ //}
+ final String state = threadState.getState();
+ if (state != null) {
+ buffer.append(" ").append(state);
+ }
+
+ buffer.append("\n java.lang.Thread.State: ").append(threadState.getJavaThreadState());
+
+ try {
+ if (vmProxy.canGetOwnedMonitorInfo() && vmProxy.canGetMonitorInfo()) {
+ List<ObjectReference> list = threadReference.ownedMonitors();
+ for (ObjectReference reference : list) {
+ if (!vmProxy.canGetMonitorFrameInfo()) { // java 5 and earlier
+ buffer.append("\n\t ").append(renderLockedObject(reference));
+ }
+ final List<ThreadReference> waiting = reference.waitingThreads();
+ for (ThreadReference thread : waiting) {
+ final String waitingThreadName = threadName(thread);
+ waitingMap.put(waitingThreadName, threadName);
+ buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.label.blocks.thread", waitingThreadName));
+ }
+ }
+ }
+
+ ObjectReference waitedMonitor = vmProxy.canGetCurrentContendedMonitor() ? threadReference.currentContendedMonitor() : null;
+ if (waitedMonitor != null) {
+ if (vmProxy.canGetMonitorInfo()) {
+ ThreadReference waitedMonitorOwner = waitedMonitor.owningThread();
+ if (waitedMonitorOwner != null) {
+ final String monitorOwningThreadName = threadName(waitedMonitorOwner);
+ waitingMap.put(threadName, monitorOwningThreadName);
+ buffer.append("\n\t ")
+ .append(DebuggerBundle.message("threads.export.attribute.label.waiting.for.thread", monitorOwningThreadName, renderObject(waitedMonitor)));
+ }
+ }
+ }
+
+ final List<StackFrame> frames = threadReference.frames();
+ hasEmptyStack = frames.size() == 0;
+
+ final TIntObjectHashMap<List<ObjectReference>> lockedAt = new TIntObjectHashMap<List<ObjectReference>>();
+ if (vmProxy.canGetMonitorFrameInfo()) {
+ for (MonitorInfo info : threadReference.ownedMonitorsAndFrames()) {
+ final int stackDepth = info.stackDepth();
+ List<ObjectReference> monitors;
+ if ((monitors = lockedAt.get(stackDepth)) == null) {
+ lockedAt.put(stackDepth, monitors = new SmartList<ObjectReference>());
+ }
+ monitors.add(info.monitor());
+ }
+ }
+
+ for (int i = 0, framesSize = frames.size(); i < framesSize; i++) {
+ final StackFrame stackFrame = frames.get(i);
+ try {
+ final Location location = stackFrame.location();
+ buffer.append("\n\t ").append(renderLocation(location));
+
+ final List<ObjectReference> monitors = lockedAt.get(i);
+ if (monitors != null) {
+ for (ObjectReference monitor : monitors) {
+ buffer.append("\n\t - ").append(renderLockedObject(monitor));
+ }
+ }
+ }
+ catch (InvalidStackFrameException e) {
+ buffer.append("\n\t Invalid stack frame: ").append(e.getMessage());
+ }
+ }
+ }
+ catch (IncompatibleThreadStateException e) {
+ buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.error.incompatible.state"));
+ }
+ threadState.setStackTrace(buffer.toString(), hasEmptyStack);
+ ThreadDumpParser.inferThreadStateDetail(threadState);
+ }
+
+ for (String waiting : waitingMap.keySet()) {
+ final ThreadState waitingThread = nameToThreadMap.get(waiting);
+ final ThreadState awaitedThread = nameToThreadMap.get(waitingMap.get(waiting));
+ awaitedThread.addWaitingThread(waitingThread);
+ }
+
+ // detect simple deadlocks
+ for (ThreadState thread : result) {
+ for (ThreadState awaitingThread : thread.getAwaitingThreads()) {
+ if (awaitingThread.isAwaitedBy(thread)) {
+ thread.addDeadlockedThread(awaitingThread);
+ awaitingThread.addDeadlockedThread(thread);
+ }
+ }
+ }
+
+ ThreadDumpParser.sortThreads(result);
+ return result;
+ }
+
+ private static String renderLockedObject(ObjectReference monitor) {
+ return DebuggerBundle.message("threads.export.attribute.label.locked", renderObject(monitor));
+ }
+
+ public static String renderObject(ObjectReference monitor) {
+ String monitorTypeName;
+ try {
+ monitorTypeName = monitor.referenceType().name();
+ }
+ catch (Throwable e) {
+ monitorTypeName = "Error getting object type: '" + e.getMessage() + "'";
+ }
+ return DebuggerBundle.message("threads.export.attribute.label.object-id", Long.toHexString(monitor.uniqueID()), monitorTypeName);
+ }
+
+ private static String threadStatusToJavaThreadState(int status) {
+ switch (status) {
+ case ThreadReference.THREAD_STATUS_MONITOR:
+ return Thread.State.BLOCKED.name();
+ case ThreadReference.THREAD_STATUS_NOT_STARTED:
+ return Thread.State.NEW.name();
+ case ThreadReference.THREAD_STATUS_RUNNING:
+ return Thread.State.RUNNABLE.name();
+ case ThreadReference.THREAD_STATUS_SLEEPING:
+ return Thread.State.TIMED_WAITING.name();
+ case ThreadReference.THREAD_STATUS_WAIT:
+ return Thread.State.WAITING.name();
+ case ThreadReference.THREAD_STATUS_ZOMBIE:
+ return Thread.State.TERMINATED.name();
+ case ThreadReference.THREAD_STATUS_UNKNOWN:
+ return "unknown";
+ default:
+ return "undefined";
+ }
+ }
+
+ private static String threadStatusToState(int status) {
+ switch (status) {
+ case ThreadReference.THREAD_STATUS_MONITOR:
+ return "waiting for monitor entry";
+ case ThreadReference.THREAD_STATUS_NOT_STARTED:
+ return "not started";
+ case ThreadReference.THREAD_STATUS_RUNNING:
+ return "runnable";
+ case ThreadReference.THREAD_STATUS_SLEEPING:
+ return "sleeping";
+ case ThreadReference.THREAD_STATUS_WAIT:
+ return "waiting";
+ case ThreadReference.THREAD_STATUS_ZOMBIE:
+ return "zombie";
+ case ThreadReference.THREAD_STATUS_UNKNOWN:
+ return "unknown";
+ default:
+ return "undefined";
+ }
+ }
+
+ public static String renderLocation(final Location location) {
+ String sourceName;
+ try {
+ sourceName = location.sourceName();
+ }
+ catch (Throwable e) {
+ sourceName = "Unknown Source";
+ }
+ return DebuggerBundle.message(
+ "export.threads.stackframe.format",
+ location.declaringType().name() + "." + location.method().name(),
+ sourceName,
+ location.lineNumber()
+ );
+ }
+
+ private static String threadName(ThreadReference threadReference) {
+ return threadReference.name() + "@" + threadReference.uniqueID();
+ }
+
+
+ public void update(AnActionEvent event){
+ Presentation presentation = event.getPresentation();
+ Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
+ if (project == null) {
+ presentation.setEnabled(false);
+ return;
+ }
+ DebuggerSession debuggerSession = (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession();
+ presentation.setEnabled(debuggerSession != null && debuggerSession.isAttached());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ThrowDebugExceptionAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ThrowDebugExceptionAction.java
new file mode 100644
index 0000000..a7c244c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ThrowDebugExceptionAction.java
@@ -0,0 +1,33 @@
+
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebugException;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAware;
+
+public class ThrowDebugExceptionAction extends AnAction implements DumbAware {
+
+ public void actionPerformed(AnActionEvent event) {
+ try{
+ throw new DebugException();
+ }
+ catch(DebugException e){
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ToggleBreakpointEnabledAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleBreakpointEnabledAction.java
new file mode 100644
index 0000000..a3f395b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleBreakpointEnabledAction.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.Nullable;
+
+public class ToggleBreakpointEnabledAction extends AnAction {
+
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = e.getData(PlatformDataKeys.PROJECT);
+ Breakpoint breakpoint = findBreakpoint(project);
+ if (breakpoint != null) {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ breakpointManager.setBreakpointEnabled(breakpoint, !breakpoint.ENABLED);
+ }
+ }
+
+ @Nullable
+ private static Breakpoint findBreakpoint(final Project project) {
+ Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ if(editor == null) {
+ return null;
+ }
+ BreakpointManager manager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ int offset = editor.getCaretModel().getOffset();
+ return manager.findBreakpoint(editor.getDocument(), offset, null);
+ }
+
+ public void update(AnActionEvent event){
+ final Presentation presentation = event.getPresentation();
+ Project project = event.getData(PlatformDataKeys.PROJECT);
+ if (project == null) {
+ presentation.setEnabled(false);
+ return;
+ }
+
+ Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ if (editor == null) {
+ presentation.setEnabled(false);
+ return;
+ }
+ PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+ if (file == null) {
+ presentation.setEnabled(false);
+ return;
+ }
+
+ FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+ final VirtualFile virtualFile = file.getVirtualFile();
+ FileType fileType = virtualFile != null ? virtualFile.getFileType() : null;
+ if (DebuggerUtils.supportsJVMDebugging(fileType) || DebuggerUtils.supportsJVMDebugging(file)) {
+ Breakpoint breakpoint = findBreakpoint(project);
+ if (breakpoint == null) {
+ presentation.setEnabled(false);
+ return;
+ }
+ presentation.setEnabled(true);
+ }
+ else {
+ presentation.setEnabled(false);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java
new file mode 100644
index 0000000..0247790
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.InstanceFilter;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.FieldBreakpoint;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.sun.jdi.Field;
+import com.sun.jdi.ObjectReference;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * User: lex
+ * Date: Sep 4, 2003
+ * Time: 8:59:30 PM
+ */
+public class ToggleFieldBreakpointAction extends AnAction {
+
+ public void actionPerformed(AnActionEvent e) {
+ Project project = e.getData(PlatformDataKeys.PROJECT);
+ if (project == null) {
+ return;
+ }
+ final SourcePosition place = getPlace(e);
+
+ if(place != null) {
+ Document document = PsiDocumentManager.getInstance(project).getDocument(place.getFile());
+ if (document != null) {
+ DebuggerManagerEx debuggerManager = DebuggerManagerEx.getInstanceEx(project);
+ BreakpointManager manager = debuggerManager.getBreakpointManager();
+ final int offset = place.getOffset();
+ final Breakpoint breakpoint = offset >= 0? manager.findBreakpoint(document, offset, FieldBreakpoint.CATEGORY) : null;
+
+ if(breakpoint == null) {
+ FieldBreakpoint fieldBreakpoint = manager.addFieldBreakpoint(document, offset);
+ if (fieldBreakpoint != null) {
+ if(DebuggerAction.isContextView(e)) {
+ final DebuggerTreeNodeImpl selectedNode = DebuggerAction.getSelectedNode(e.getDataContext());
+ if (selectedNode != null && selectedNode.getDescriptor() instanceof FieldDescriptorImpl) {
+ ObjectReference object = ((FieldDescriptorImpl)selectedNode.getDescriptor()).getObject();
+ if(object != null) {
+ long id = object.uniqueID();
+ InstanceFilter[] instanceFilters = new InstanceFilter[] { InstanceFilter.create(Long.toString(id))};
+ fieldBreakpoint.setInstanceFilters(instanceFilters);
+ fieldBreakpoint.INSTANCE_FILTERS_ENABLED = true;
+ }
+ }
+ }
+
+ RequestManagerImpl.createRequests(fieldBreakpoint);
+
+ manager.editBreakpoint(fieldBreakpoint, PlatformDataKeys.EDITOR.getData(e.getDataContext()));
+ }
+ }
+ else {
+ manager.removeBreakpoint(breakpoint);
+ }
+ }
+ }
+ }
+
+ public void update(AnActionEvent event){
+ SourcePosition place = getPlace(event);
+ boolean toEnable = place != null;
+
+ Presentation presentation = event.getPresentation();
+ if(ActionPlaces.PROJECT_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.STRUCTURE_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.FAVORITES_VIEW_POPUP.equals(event.getPlace())) {
+ presentation.setVisible(toEnable);
+ }
+ else if(DebuggerAction.isContextView(event)) {
+ presentation.setText(DebuggerBundle.message("action.add.field.watchpoint.text"));
+ Project project = event.getData(PlatformDataKeys.PROJECT);
+ if(project != null && place != null) {
+ Document document = PsiDocumentManager.getInstance(project).getDocument(place.getFile());
+ if (document != null) {
+ final int offset = place.getOffset();
+ final BreakpointManager breakpointManager = (DebuggerManagerEx.getInstanceEx(project)).getBreakpointManager();
+ final Breakpoint fieldBreakpoint = offset >= 0 ? breakpointManager.findBreakpoint(document, offset, FieldBreakpoint.CATEGORY) : null;
+ if (fieldBreakpoint != null) {
+ presentation.setEnabled(false);
+ return;
+ }
+ }
+ }
+ }
+ presentation.setVisible(toEnable);
+ }
+
+ @Nullable
+ public static SourcePosition getPlace(AnActionEvent event) {
+ final DataContext dataContext = event.getDataContext();
+ Project project = event.getData(PlatformDataKeys.PROJECT);
+ if(project == null) {
+ return null;
+ }
+ if (ActionPlaces.PROJECT_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.STRUCTURE_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.FAVORITES_VIEW_POPUP.equals(event.getPlace())) {
+ final PsiElement psiElement = event.getData(LangDataKeys.PSI_ELEMENT);
+ if(psiElement instanceof PsiField) {
+ return SourcePosition.createFromElement(psiElement);
+ }
+ return null;
+ }
+
+ final DebuggerTreeNodeImpl selectedNode = DebuggerAction.getSelectedNode(dataContext);
+ if(selectedNode != null && selectedNode.getDescriptor() instanceof FieldDescriptorImpl) {
+ FieldDescriptorImpl descriptor = (FieldDescriptorImpl)selectedNode.getDescriptor();
+ return descriptor.getSourcePosition(project, DebuggerAction.getDebuggerContext(dataContext));
+ }
+
+ if(DebuggerAction.isContextView(event)) {
+ DebuggerTree tree = DebuggerTree.DATA_KEY.getData(dataContext);
+ if(tree != null && tree.getSelectionPath() != null) {
+ DebuggerTreeNodeImpl node = ((DebuggerTreeNodeImpl)tree.getSelectionPath().getLastPathComponent());
+ if(node != null && node.getDescriptor() instanceof FieldDescriptorImpl) {
+ Field field = ((FieldDescriptorImpl)node.getDescriptor()).getField();
+ DebuggerSession session = tree.getDebuggerContext().getDebuggerSession();
+ PsiClass psiClass = DebuggerUtilsEx.findClass(field.declaringType().name(), project, (session != null) ? session.getSearchScope(): GlobalSearchScope.allScope(project));
+ if(psiClass != null) {
+ psiClass = (PsiClass) psiClass.getNavigationElement();
+ final PsiField psiField = psiClass.findFieldByName(field.name(), true);
+ if (psiField != null) {
+ return SourcePosition.createFromElement(psiField);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ Editor editor = event.getData(PlatformDataKeys.EDITOR);
+ if(editor == null) {
+ editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ }
+ if (editor != null) {
+ final Document document = editor.getDocument();
+ PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
+ if (file != null) {
+ FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+ final VirtualFile virtualFile = file.getVirtualFile();
+ FileType fileType = virtualFile != null ? virtualFile.getFileType() : null;
+ if (StdFileTypes.JAVA == fileType || StdFileTypes.CLASS == fileType) {
+ final PsiField field = FieldBreakpoint.findField(project, document, editor.getCaretModel().getOffset());
+ if(field != null){
+ return SourcePosition.createFromElement(field);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ToggleLineBreakpointActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleLineBreakpointActionHandler.java
new file mode 100644
index 0000000..5b56156
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleLineBreakpointActionHandler.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.codeInsight.folding.impl.actions.ExpandRegionAction;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class ToggleLineBreakpointActionHandler extends DebuggerActionHandler {
+
+ public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
+ PlaceInDocument place = getPlace(project, event);
+ if (place != null) {
+ final Document document = place.getDocument();
+ final int offset = place.getOffset();
+ int line = document.getLineNumber(offset);
+
+ VirtualFile file = FileDocumentManager.getInstance().getFile(document);
+ PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
+ if (DebuggerUtils.supportsJVMDebugging(file.getFileType()) || DebuggerUtils.supportsJVMDebugging(psiFile)) {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ return breakpointManager.findBreakpoint(document, offset, LineBreakpoint.CATEGORY) != null ||
+ LineBreakpoint.canAddLineBreakpoint(project, document, line);
+ }
+ }
+
+ return false;
+ }
+
+ public void perform(@NotNull final Project project, final AnActionEvent event) {
+ PlaceInDocument place = getPlace(project, event);
+ if(place == null) {
+ return;
+ }
+
+ ExpandRegionAction.expandRegionAtCaret(project, event.getData(PlatformDataKeys.EDITOR));
+
+ Document document = place.getDocument();
+ int line = document.getLineNumber(place.getOffset());
+
+ DebuggerManagerEx debugManager = DebuggerManagerEx.getInstanceEx(project);
+ if (debugManager == null) {
+ return;
+ }
+ BreakpointManager manager = debugManager.getBreakpointManager();
+ final Breakpoint breakpoint = manager.findBreakpoint(document, place.getOffset(), LineBreakpoint.CATEGORY);
+ if(breakpoint == null) {
+ LineBreakpoint lineBreakpoint = manager.addLineBreakpoint(document, line);
+ if(lineBreakpoint != null) {
+ RequestManagerImpl.createRequests(lineBreakpoint);
+ }
+ } else {
+ manager.removeBreakpoint(breakpoint);
+ }
+ }
+
+ @Nullable
+ private static PlaceInDocument getPlace(@NotNull final Project project, AnActionEvent event) {
+ Editor editor = event.getData(PlatformDataKeys.EDITOR);
+ if(editor == null) {
+ editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ }
+ if (editor != null) {
+ final Document document = editor.getDocument();
+ PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
+ if (file != null) {
+ final Editor editor1 = editor;
+ return new PlaceInDocument() {
+ public Document getDocument() {
+ return document;
+ }
+
+ public int getOffset() {
+ return editor1.getCaretModel().getOffset();
+ }
+ };
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ToggleMethodBreakpointAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleMethodBreakpointAction.java
new file mode 100644
index 0000000..80992e3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleMethodBreakpointAction.java
@@ -0,0 +1,154 @@
+/*
+ * 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 ToggleMethodBreakpointAction
+ * @author Jeka
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.MethodBreakpoint;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.util.text.CharArrayUtil;
+import org.jetbrains.annotations.Nullable;
+
+public class ToggleMethodBreakpointAction extends AnAction {
+
+ public void update(AnActionEvent event){
+ boolean toEnable = getPlace(event) != null;
+
+ if (ActionPlaces.isPopupPlace(event.getPlace())) {
+ event.getPresentation().setVisible(toEnable);
+ }
+ else {
+ event.getPresentation().setEnabled(toEnable);
+ }
+ }
+
+
+ public void actionPerformed(AnActionEvent e) {
+ Project project = e.getData(PlatformDataKeys.PROJECT);
+ if (project == null) {
+ return;
+ }
+ DebuggerManagerEx debugManager = DebuggerManagerEx.getInstanceEx(project);
+ if (debugManager == null) {
+ return;
+ }
+ final BreakpointManager manager = debugManager.getBreakpointManager();
+ final PlaceInDocument place = getPlace(e);
+ if(place != null) {
+ Breakpoint breakpoint = manager.findBreakpoint(place.getDocument(), place.getOffset(), MethodBreakpoint.CATEGORY);
+ if(breakpoint == null) {
+ final int methodLine = place.getDocument().getLineNumber(place.getOffset());
+ MethodBreakpoint methodBreakpoint = manager.addMethodBreakpoint(place.getDocument(), methodLine);
+ if(methodBreakpoint != null) {
+ RequestManagerImpl.createRequests(methodBreakpoint);
+ }
+ }
+ else {
+ manager.removeBreakpoint(breakpoint);
+ }
+ }
+ }
+
+ @Nullable
+ private static PlaceInDocument getPlace(AnActionEvent event) {
+ final Project project = event.getData(PlatformDataKeys.PROJECT);
+ if(project == null) {
+ return null;
+ }
+
+ PsiElement method = null;
+ Document document = null;
+
+ if (ActionPlaces.PROJECT_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.STRUCTURE_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.FAVORITES_VIEW_POPUP.equals(event.getPlace()) ||
+ ActionPlaces.NAVIGATION_BAR.equals(event.getPlace())) {
+ final PsiElement psiElement = event.getData(LangDataKeys.PSI_ELEMENT);
+ if(psiElement instanceof PsiMethod) {
+ final PsiFile containingFile = psiElement.getContainingFile();
+ if (containingFile != null) {
+ method = psiElement;
+ document = PsiDocumentManager.getInstance(project).getDocument(containingFile);
+ }
+ }
+ }
+ else {
+ Editor editor = event.getData(PlatformDataKeys.EDITOR);
+ if(editor == null) {
+ editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ }
+ if (editor != null) {
+ document = editor.getDocument();
+ PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
+ if (file != null) {
+ final VirtualFile virtualFile = file.getVirtualFile();
+ FileType fileType = virtualFile != null ? virtualFile.getFileType() : null;
+ if (StdFileTypes.JAVA == fileType || StdFileTypes.CLASS == fileType) {
+ method = findMethod(project, editor);
+ }
+ }
+ }
+ }
+
+ if(method != null) {
+ final PsiElement method1 = method;
+ final Document document1 = document;
+
+ return new PlaceInDocument() {
+ public Document getDocument() {
+ return document1;
+ }
+
+ public int getOffset() {
+ return method1.getTextOffset();
+ }
+ };
+ }
+ return null;
+ }
+
+ @Nullable
+ private static PsiMethod findMethod(Project project, Editor editor) {
+ if (editor == null) {
+ return null;
+ }
+ PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+ if(psiFile == null) {
+ return null;
+ }
+ final int offset = CharArrayUtil.shiftForward(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset(), " \t");
+ return DebuggerUtilsEx.findPsiMethod(psiFile, offset);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java b/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java
new file mode 100644
index 0000000..6847590
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.DumbAware;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 11:05:57 PM
+ */
+public class ViewAsGroup extends ActionGroup implements DumbAware {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.actions.ViewAsGroup");
+
+ private AnAction[] myChildren = AnAction.EMPTY_ARRAY;
+
+ public ViewAsGroup() {
+ super(null, true);
+ }
+
+ private static class RendererAction extends ToggleAction {
+ private final NodeRenderer myNodeRenderer;
+
+ public RendererAction(NodeRenderer nodeRenderer) {
+ super(nodeRenderer.getName());
+ myNodeRenderer = nodeRenderer;
+ }
+
+ public boolean isSelected(AnActionEvent e) {
+ DebuggerTreeNodeImpl[] nodes = DebuggerAction.getSelectedNodes(e.getDataContext());
+ if (nodes == null) {
+ return false;
+ }
+ for (DebuggerTreeNodeImpl node : nodes) {
+ if (node.getDescriptor() instanceof ValueDescriptorImpl) {
+ if (((ValueDescriptorImpl)node.getDescriptor()).getLastRenderer() != myNodeRenderer) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public void setSelected(final AnActionEvent e, final boolean state) {
+ final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
+ final DebuggerTreeNodeImpl[] nodes = DebuggerAction.getSelectedNodes(e.getDataContext());
+
+ LOG.assertTrue(debuggerContext != null && nodes != null);
+
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public void threadAction() {
+ for (final DebuggerTreeNodeImpl node : nodes) {
+ if (node.getDescriptor() instanceof ValueDescriptorImpl) {
+ final ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)node.getDescriptor();
+ if (state) {
+ valueDescriptor.setRenderer(myNodeRenderer);
+ node.calcRepresentation();
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public AnAction[] getChildren(@Nullable final AnActionEvent e) {
+ return myChildren;
+ }
+
+ private static AnAction [] calcChildren(DebuggerTreeNodeImpl[] nodes) {
+ List<AnAction> renderers = new ArrayList<AnAction>();
+
+ List<NodeRenderer> allRenderers = NodeRendererSettings.getInstance().getAllRenderers();
+
+ boolean anyValueDescriptor = false;
+
+ for (NodeRenderer nodeRenderer : allRenderers) {
+ boolean allApp = true;
+
+ for (DebuggerTreeNodeImpl node : nodes) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (descriptor instanceof ValueDescriptorImpl) {
+ anyValueDescriptor = true;
+ ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
+ if (valueDescriptor.isValueValid() && !nodeRenderer.isApplicable(valueDescriptor.getType())) {
+ allApp = false;
+ break;
+ }
+ }
+ }
+
+ if (!anyValueDescriptor) {
+ return AnAction.EMPTY_ARRAY;
+ }
+
+ if (allApp) {
+ renderers.add(new RendererAction(nodeRenderer));
+ }
+ }
+
+ List<AnAction> children = new ArrayList<AnAction>();
+ AnAction[] viewAsActions = ((DefaultActionGroup) ActionManager.getInstance().getAction(DebuggerActions.REPRESENTATION_LIST)).getChildren(null);
+ for (AnAction viewAsAction : viewAsActions) {
+ if (viewAsAction instanceof AutoRendererAction) {
+ if (renderers.size() > 1) {
+ viewAsAction.getTemplatePresentation().setVisible(true);
+ children.add(viewAsAction);
+ }
+ }
+ else {
+ children.add(viewAsAction);
+ }
+ }
+
+ children.add(Separator.getInstance());
+ children.addAll(renderers);
+
+ return children.toArray(new AnAction[children.size()]);
+ }
+
+ public void update(final AnActionEvent event) {
+ if(!DebuggerAction.isFirstStart(event)) {
+ return;
+ }
+
+ final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(event.getDataContext());
+ final DebuggerTreeNodeImpl[] selectedNodes = DebuggerAction.getSelectedNodes(event.getDataContext());
+
+ final DebugProcessImpl process = debuggerContext.getDebugProcess();
+ if (process == null) {
+ event.getPresentation().setEnabled(false);
+ return;
+ }
+
+ process.getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public void threadAction() {
+ myChildren = calcChildren(selectedNodes);
+ DebuggerAction.enableAction(event, myChildren.length > 0);
+ }
+ });
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java
new file mode 100644
index 0000000..e195ff9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.actions;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.impl.DocumentImpl;
+import com.intellij.openapi.fileTypes.FileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.ui.EditorTextField;
+
+import javax.swing.*;
+import java.awt.*;
+
+/*
+ * @author Jeka
+ */
+public class ViewTextAction extends BaseValueAction {
+ protected void processText(final Project project, final String text, DebuggerTreeNodeImpl node, DebuggerContextImpl debuggerContext) {
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ final String labelText = descriptor instanceof ValueDescriptorImpl? ((ValueDescriptorImpl)descriptor).getValueLabel() : null;
+ final MyDialog dialog = new MyDialog(project);
+ dialog.setTitle(labelText != null? "View Text for: " + labelText : "View Text");
+ dialog.setText(text);
+ dialog.show();
+ }
+
+ private static class MyDialog extends DialogWrapper {
+
+ private EditorTextField myTextViewer;
+
+ private MyDialog(Project project) {
+ super(project, false);
+ setModal(false);
+ setCancelButtonText("Close");
+ setCrossClosesWindow(true);
+
+ myTextViewer = new TextViewer(project);
+ init();
+ }
+
+ public void setText(String text) {
+ myTextViewer.setText(text);
+ }
+
+ protected Action[] createActions() {
+ return new Action[] {getCancelAction()};
+ }
+
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.debugger.actions.ViewTextAction";
+ }
+
+ protected JComponent createCenterPanel() {
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.add(myTextViewer, BorderLayout.CENTER);
+ panel.setPreferredSize(new Dimension(300, 200));
+ return panel;
+ }
+
+ }
+
+
+ private static class TextViewer extends EditorTextField {
+
+ private TextViewer(Project project) {
+ super(createDocument(), project, FileTypes.PLAIN_TEXT, true, false);
+ }
+
+ private static Document createDocument() {
+ final Document document = EditorFactory.getInstance().createDocument("");
+ if (document instanceof DocumentImpl) {
+ ((DocumentImpl)document).setAcceptSlashR(true);
+ }
+ return document;
+ }
+
+ protected EditorEx createEditor() {
+ final EditorEx editor = super.createEditor();
+ editor.setHorizontalScrollbarVisible(true);
+ editor.setVerticalScrollbarVisible(true);
+ editor.setEmbeddedIntoDialogWrapper(true);
+ editor.getComponent().setPreferredSize(null);
+ return editor;
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/apiAdapters/ConnectionServiceWrapper.java b/java/debugger/impl/src/com/intellij/debugger/apiAdapters/ConnectionServiceWrapper.java
new file mode 100644
index 0000000..150501e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/apiAdapters/ConnectionServiceWrapper.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.apiAdapters;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.util.ArrayUtil;
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.VirtualMachineManager;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * @author max
+ */
+public class ConnectionServiceWrapper {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.apiAdapters.ConnectionService");
+
+ private static Class myDelegateClass;
+ private final Object myConnection;
+
+ static {
+ try {
+ //noinspection HardCodedStringLiteral
+ myDelegateClass = SystemInfo.JAVA_VERSION.startsWith("1.4")
+ ? Class.forName("com.sun.tools.jdi.ConnectionService")
+ : Class.forName("com.sun.jdi.connect.spi.Connection");
+ }
+ catch (ClassNotFoundException e) {
+ LOG.error(e);
+ }
+ }
+
+ public ConnectionServiceWrapper(final Object connection) {
+ myConnection = connection;
+ }
+
+ public void close() throws IOException {
+ try {
+ //noinspection HardCodedStringLiteral
+ final Method method = myDelegateClass.getMethod("close", ArrayUtil.EMPTY_CLASS_ARRAY);
+ method.invoke(myConnection, ArrayUtil.EMPTY_OBJECT_ARRAY);
+ }
+ catch (NoSuchMethodException e) {
+ LOG.error(e);
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException)cause;
+ }
+ LOG.error(e);
+ }
+ }
+
+ public VirtualMachine createVirtualMachine() throws IOException {
+ try {
+ final VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();
+ //noinspection HardCodedStringLiteral
+ final Method method = virtualMachineManager.getClass().getMethod("createVirtualMachine", new Class[]{myDelegateClass});
+ return (VirtualMachine)method.invoke(virtualMachineManager, new Object[]{myConnection});
+ }
+ catch (NoSuchMethodException e) {
+ LOG.error(e);
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException)cause;
+ }
+ if (cause instanceof VMDisconnectedException) {
+ return null; // ignore this one
+ }
+ LOG.error(e);
+ }
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/apiAdapters/TransportServiceWrapper.java b/java/debugger/impl/src/com/intellij/debugger/apiAdapters/TransportServiceWrapper.java
new file mode 100644
index 0000000..a464864
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/apiAdapters/TransportServiceWrapper.java
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.apiAdapters;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.util.ArrayUtil;
+import com.sun.jdi.connect.Transport;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author max
+ */
+public class TransportServiceWrapper {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.apiAdapters.TransportService");
+
+ private final Object myDelegateObject;
+ private final Class myDelegateClass;
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static final String SOCKET_TRANSPORT_CLASS = SystemInfo.JAVA_VERSION.startsWith("1.4")
+ ? "com.sun.tools.jdi.SocketTransport"
+ : "com.sun.tools.jdi.SocketTransportService";
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static final String SHMEM_TRANSPORT_CLASS = SystemInfo.JAVA_VERSION.startsWith("1.4")
+ ? "com.sun.tools.jdi.SharedMemoryTransport"
+ : "com.sun.tools.jdi.SharedMemoryTransportService";
+
+ private final Map<String, Object> myListenAddresses = new HashMap<String, Object>();
+
+ private TransportServiceWrapper(Class delegateClass) throws NoSuchMethodException,
+ IllegalAccessException,
+ InvocationTargetException,
+ InstantiationException {
+ myDelegateClass = delegateClass;
+ final Constructor constructor = delegateClass.getDeclaredConstructor(ArrayUtil.EMPTY_CLASS_ARRAY);
+ constructor.setAccessible(true);
+ myDelegateObject = constructor.newInstance(ArrayUtil.EMPTY_OBJECT_ARRAY);
+ }
+
+ /**
+ * Applicable if IDEA is run on JDK 1.4.2.x only!
+ * @param transportObj
+ */
+ private TransportServiceWrapper(Transport transportObj) {
+ myDelegateClass = transportObj.getClass();
+ myDelegateObject = transportObj;
+ }
+
+ public ConnectionServiceWrapper attach(final String s) throws IOException {
+ try {
+ // Applicable if IDEA is run on JDK 1.4.2.x only!
+ // in JDK 1.5 the signature of the "attach" method has been changed to "attach(String, long, long)"
+ //noinspection HardCodedStringLiteral
+ final Method method = myDelegateClass.getMethod("attach", new Class[]{String.class});
+ method.setAccessible(true);
+ return new ConnectionServiceWrapper(method.invoke(myDelegateObject, new Object[]{s}));
+ }
+ catch (NoSuchMethodException e) {
+ LOG.error(e);
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException)cause;
+ }
+ LOG.error(e);
+ }
+ return null;
+ }
+
+ public String startListening() throws IOException {
+ try {
+ //noinspection HardCodedStringLiteral
+ final Method method = myDelegateClass.getMethod("startListening", ArrayUtil.EMPTY_CLASS_ARRAY);
+ method.setAccessible(true);
+ final Object rv = method.invoke(myDelegateObject, ArrayUtil.EMPTY_OBJECT_ARRAY);
+ // important! do not cast to string cause return types differ in jdk 1.4 and jdk 1.5
+ final String strValue = rv.toString();
+ myListenAddresses.put(strValue, rv);
+ return strValue;
+ }
+ catch (NoSuchMethodException e) {
+ LOG.error(e);
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException)cause;
+ }
+ LOG.error(e);
+ }
+ return null;
+ }
+
+ public void stopListening(final String address) throws IOException {
+ try {
+ Object value = myListenAddresses.get(address);
+ if (value == null) {
+ value = address;
+ }
+ Class paramClass = value.getClass();
+ for (Class superClass = paramClass.getSuperclass(); !Object.class.equals(superClass); superClass = superClass.getSuperclass()) {
+ paramClass = superClass;
+ }
+ //noinspection HardCodedStringLiteral
+ final Method method = myDelegateClass.getMethod("stopListening", new Class[] {paramClass});
+ method.setAccessible(true);
+ method.invoke(myDelegateObject, new Object[]{value});
+ }
+ catch (NoSuchMethodException e) {
+ LOG.error(e);
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException)cause;
+ }
+ LOG.error(e);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public String transportId() {
+ if (SOCKET_TRANSPORT_CLASS.equals(myDelegateClass.getName())) {
+ return "dt_socket";
+ }
+ else if (SHMEM_TRANSPORT_CLASS.equals(myDelegateClass.getName())) {
+ return "dt_shmem";
+ }
+
+ LOG.error("Unknown serivce");
+ return "<unknown>";
+ }
+
+ public static TransportServiceWrapper getTransportService(boolean forceSocketTransport) throws ExecutionException {
+ TransportServiceWrapper transport;
+ try {
+ try {
+ if (forceSocketTransport) {
+ transport = new TransportServiceWrapper(Class.forName(SOCKET_TRANSPORT_CLASS));
+ }
+ else {
+ transport = new TransportServiceWrapper(Class.forName(SHMEM_TRANSPORT_CLASS));
+ }
+ }
+ catch (UnsatisfiedLinkError e) {
+ transport = new TransportServiceWrapper(Class.forName(SOCKET_TRANSPORT_CLASS));
+ }
+ }
+ catch (Exception e) {
+ throw new ExecutionException(e.getClass().getName() + " : " + e.getMessage());
+ }
+ return transport;
+ }
+
+ /**
+ * Applicable if IDEA is run on JDK 1.4.2.x only!
+ * @param transportObject
+ * @return transport service wrapper
+ */
+ public static TransportServiceWrapper getTransportService(Transport transportObject){
+ return new TransportServiceWrapper(transportObject);
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java b/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java
new file mode 100644
index 0000000..346ff0b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.codeinsight;
+
+import com.intellij.codeInsight.CodeInsightBundle;
+import com.intellij.codeInsight.generation.surroundWith.JavaExpressionSurrounder;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ScrollType;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * User: lex
+ * Date: Jul 17, 2003
+ * Time: 7:51:01 PM
+ */
+public class JavaWithRuntimeCastSurrounder extends JavaExpressionSurrounder {
+
+ public String getTemplateDescription() {
+ return CodeInsightBundle.message("surround.with.runtime.type.template");
+ }
+
+ public boolean isApplicable(PsiExpression expr) {
+ if (!expr.isPhysical()) return false;
+ PsiFile file = expr.getContainingFile();
+ if (!(file instanceof PsiCodeFragment)) return false;
+ if (file.getUserData(DebuggerExpressionComboBox.KEY) == null) {
+ return false;
+ }
+
+ return RuntimeTypeEvaluator.isSubtypeable(expr);
+ }
+
+ public TextRange surroundExpression(Project project, Editor editor, PsiExpression expr) throws IncorrectOperationException {
+ DebuggerContextImpl debuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
+ if (debuggerSession != null) {
+ final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, expr.getProject());
+ SurroundWithCastWorker worker = new SurroundWithCastWorker(editor, expr, debuggerContext, progressWindow);
+ progressWindow.setTitle(DebuggerBundle.message("title.evaluating"));
+ debuggerContext.getDebugProcess().getManagerThread().startProgress(worker, progressWindow);
+ }
+ return null;
+ }
+
+ private class SurroundWithCastWorker extends RuntimeTypeEvaluator {
+ private final Editor myEditor;
+
+ public SurroundWithCastWorker(Editor editor, PsiExpression expression, DebuggerContextImpl context, final ProgressIndicator indicator) {
+ super(editor, expression, context, indicator);
+ myEditor = editor;
+ }
+
+ @Override
+ protected void typeCalculationFinished(@Nullable final PsiClass type) {
+ if (type == null) {
+ return;
+ }
+
+ hold();
+ final Project project = myElement.getProject();
+ DebuggerInvocationUtil.invokeLater(project, new Runnable() {
+ public void run() {
+ new WriteCommandAction(project, CodeInsightBundle.message("command.name.surround.with.runtime.cast")) {
+ protected void run(Result result) throws Throwable {
+ try {
+ PsiElementFactory factory = JavaPsiFacade.getInstance(myElement.getProject()).getElementFactory();
+ PsiParenthesizedExpression parenth =
+ (PsiParenthesizedExpression)factory.createExpressionFromText("((" + type.getQualifiedName() + ")expr)", null);
+ PsiTypeCastExpression cast = (PsiTypeCastExpression)parenth.getExpression();
+ cast.getOperand().replace(myElement);
+ parenth = (PsiParenthesizedExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(parenth);
+ PsiExpression expr = (PsiExpression)myElement.replace(parenth);
+ TextRange range = expr.getTextRange();
+ myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
+ myEditor.getCaretModel().moveToOffset(range.getEndOffset());
+ myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
+ }
+ catch (IncorrectOperationException e) {
+ // OK here. Can be caused by invalid type like one for proxy starts with . '.Proxy34'
+ }
+ finally {
+ release();
+ }
+ }
+ }.execute();
+ }
+ }, myProgressIndicator.getModalityState());
+ }
+
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java
new file mode 100644
index 0000000..ecb7556
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.codeinsight;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.EvaluatingComputable;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.EditorEvaluationCommand;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.InterfaceType;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author peter
+ */
+public abstract class RuntimeTypeEvaluator extends EditorEvaluationCommand<PsiClass> {
+ public RuntimeTypeEvaluator(@Nullable Editor editor, PsiElement expression, DebuggerContextImpl context, final ProgressIndicator indicator) {
+ super(editor, expression, context, indicator);
+ }
+
+ public void threadAction() {
+ PsiClass type = null;
+ try {
+ type = evaluate();
+ }
+ catch (ProcessCanceledException ignored) {
+ }
+ catch (EvaluateException ignored) {
+ }
+ finally {
+ typeCalculationFinished(type);
+ }
+ }
+
+ protected abstract void typeCalculationFinished(@Nullable PsiClass type);
+
+ @Nullable
+ protected PsiClass evaluate(final EvaluationContextImpl evaluationContext) throws EvaluateException {
+ final Project project = evaluationContext.getProject();
+
+ ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable<ExpressionEvaluator>() {
+ public ExpressionEvaluator compute() throws EvaluateException {
+ return EvaluatorBuilderImpl.getInstance().build(myElement, ContextUtil.getSourcePosition(evaluationContext));
+ }
+ });
+
+ final Value value = evaluator.evaluate(evaluationContext);
+ if(value != null){
+ return getCastableRuntimeType(project, value);
+ }
+
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.surrounded.expression.null"));
+ }
+
+ public static PsiClass getCastableRuntimeType(Project project, Value value) {
+ Type type = value.type();
+ PsiClass psiClass = findPsiClass(project, type);
+ if (psiClass != null) {
+ return psiClass;
+ }
+
+ if (type instanceof ClassType) {
+ ClassType superclass = ((ClassType)type).superclass();
+ if (superclass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(superclass.name())) {
+ psiClass = findPsiClass(project, superclass);
+ if (psiClass != null) {
+ return psiClass;
+ }
+ }
+
+ for (InterfaceType interfaceType : ((ClassType)type).interfaces()) {
+ psiClass = findPsiClass(project, interfaceType);
+ if (psiClass != null) {
+ return psiClass;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static PsiClass findPsiClass(Project project, Type type) {
+ AccessToken token = ReadAction.start();
+ try {
+ return JavaPsiFacade.getInstance(project).findClass(type.name().replace('$', '.'), GlobalSearchScope.allScope(project));
+ }
+ finally {
+ token.finish();
+ }
+ }
+
+ public static boolean isSubtypeable(PsiExpression expr) {
+ final PsiType type = expr.getType();
+ if (type instanceof PsiPrimitiveType) {
+ return false;
+ }
+ if (type instanceof PsiClassType) {
+ final PsiClass psiClass = ((PsiClassType)type).resolve();
+ if (psiClass != null && psiClass.hasModifierProperty(PsiModifier.FINAL)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java b/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java
new file mode 100644
index 0000000..49d3cae
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.NoDataException;
+import com.intellij.debugger.PositionManager;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.Location;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.request.ClassPrepareRequest;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CompoundPositionManager implements PositionManager{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.CompoundPositionManager");
+ private final ArrayList<PositionManager> myPositionManagers = new ArrayList<PositionManager>();
+
+ public CompoundPositionManager() {
+ }
+
+ public CompoundPositionManager(PositionManager manager) {
+ appendPositionManager(manager);
+ }
+
+ public void appendPositionManager(PositionManager manager) {
+ myPositionManagers.remove(manager);
+ myPositionManagers.add(0, manager);
+ }
+
+ public SourcePosition getSourcePosition(Location location) {
+ for (PositionManager positionManager : myPositionManagers) {
+ try {
+ return positionManager.getSourcePosition(location);
+ }
+ catch (NoDataException ignored) {
+ }
+ }
+ return null;
+ }
+
+ @NotNull
+ public List<ReferenceType> getAllClasses(SourcePosition classPosition) {
+ for (PositionManager positionManager : myPositionManagers) {
+ try {
+ return positionManager.getAllClasses(classPosition);
+ }
+ catch (NoDataException ignored) {
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @NotNull
+ public List<Location> locationsOfLine(ReferenceType type, SourcePosition position) {
+ for (PositionManager positionManager : myPositionManagers) {
+ try {
+ return positionManager.locationsOfLine(type, position);
+ }
+ catch (NoDataException ignored) {
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ public ClassPrepareRequest createPrepareRequest(ClassPrepareRequestor requestor, SourcePosition position) {
+ for (PositionManager positionManager : myPositionManagers) {
+ try {
+ return positionManager.createPrepareRequest(requestor, position);
+ }
+ catch (NoDataException ignored) {
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java
new file mode 100644
index 0000000..bfd7d05
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.IndexNotReadyException;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.Location;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class ContextUtil {
+ public static final Key<Boolean> IS_JSP_IMPLICIT = new Key<Boolean>("JspImplicit");
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.PositionUtil");
+
+ @Nullable
+ public static SourcePosition getSourcePosition(final StackFrameContext context) {
+ if (context == null) {
+ return null;
+ }
+ DebugProcessImpl debugProcess = (DebugProcessImpl)context.getDebugProcess();
+ if(debugProcess == null) {
+ return null;
+ }
+ final StackFrameProxy frameProxy = context.getFrameProxy();
+ if(frameProxy == null) {
+ return null;
+ }
+ Location location = null;
+ try {
+ location = frameProxy.location();
+ }
+ catch (Throwable th) {
+ LOG.debug(th);
+ }
+ final CompoundPositionManager positionManager = debugProcess.getPositionManager();
+ if (positionManager == null) {
+ // process already closed
+ return null;
+ }
+ try {
+ return positionManager.getSourcePosition(location);
+ } catch (IndexNotReadyException e) {
+ return null;
+ }
+ }
+
+ @Nullable
+ public static PsiElement getContextElement(final StackFrameContext context) {
+ return getContextElement(context, getSourcePosition(context));
+ }
+
+ @Nullable
+ protected static PsiElement getContextElement(final StackFrameContext context, final SourcePosition position) {
+ if(LOG.isDebugEnabled()) {
+ final SourcePosition sourcePosition = getSourcePosition(context);
+ LOG.assertTrue(Comparing.equal(sourcePosition, position));
+ }
+
+ final PsiElement element = getContextElement(position);
+
+ if(element == null) {
+ return null;
+ }
+
+ final StackFrameProxyImpl frameProxy = (StackFrameProxyImpl)context.getFrameProxy();
+
+ if(frameProxy == null) {
+ return element;
+ }
+
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ List<LocalVariableProxyImpl> list = frameProxy.visibleVariables();
+
+ PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(element.getProject()).getResolveHelper();
+ buf.append('{');
+ for (LocalVariableProxyImpl localVariable : list) {
+ final String varName = localVariable.name();
+ if (resolveHelper.resolveReferencedVariable(varName, element) == null) {
+ buf.append(localVariable.getVariable().typeName()).append(" ").append(varName).append(";");
+ }
+ }
+ buf.append('}');
+
+ if (buf.length() <= 2) {
+ return element;
+ }
+ final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(element.getProject()).getElementFactory();
+ final PsiCodeBlock codeBlockFromText = elementFactory.createCodeBlockFromText(buf.toString(), element);
+
+ final PsiStatement[] statements = codeBlockFromText.getStatements();
+ for (PsiStatement statement : statements) {
+ if (statement instanceof PsiDeclarationStatement) {
+ PsiDeclarationStatement declStatement = (PsiDeclarationStatement)statement;
+ PsiElement[] declaredElements = declStatement.getDeclaredElements();
+ for (PsiElement declaredElement : declaredElements) {
+ declaredElement.putUserData(IS_JSP_IMPLICIT, Boolean.TRUE);
+ }
+ }
+ }
+ return codeBlockFromText;
+ }
+ catch (IncorrectOperationException e) {
+ return element;
+ }
+ catch (EvaluateException e) {
+ return element;
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ @Nullable
+ public static PsiElement getContextElement(final SourcePosition position) {
+ return position == null ? null :position.getElementAt();
+ }
+
+ public static boolean isJspImplicit(PsiElement element) {
+ return Boolean.TRUE.equals(element.getUserData(IS_JSP_IMPLICIT));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java
new file mode 100644
index 0000000..cc5a975
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.sun.jdi.ThreadReference;
+
+/**
+ * @author lex
+ */
+public class DebugProcessAdapterImpl implements DebugProcessListener {
+ //executed in manager thread
+ public final void paused(SuspendContext suspendContext) {
+ paused(((SuspendContextImpl)suspendContext));
+ }
+
+ //executed in manager thread
+ public final void resumed(SuspendContext suspendContext) {
+ resumed(((SuspendContextImpl)suspendContext));
+ }
+
+ //executed in manager thread
+ public final void processDetached(DebugProcess process, boolean closedByUser) {
+ processDetached(((DebugProcessImpl)process), closedByUser);
+ }
+
+ //executed in manager thread
+ public final void processAttached(DebugProcess process) {
+ processAttached(((DebugProcessImpl)process));
+ }
+
+ //executed in manager thread
+ public void connectorIsReady() {
+ }
+
+ public void paused(SuspendContextImpl suspendContext) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //executed in manager thread
+ public void resumed(SuspendContextImpl suspendContext) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //executed in manager thread
+ public void processDetached(DebugProcessImpl process, boolean closedByUser) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //executed in manager thread
+ public void processAttached(DebugProcessImpl process) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //executed in manager thread
+ public void threadStarted(DebugProcess proc, ThreadReference thread) {
+ }
+
+ //executed in manager thread
+ public void threadStopped(DebugProcess proc, ThreadReference thread) {
+ }
+
+ public void attachException(RunProfileState state, ExecutionException exception, RemoteConnection remoteConnection) {
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java
new file mode 100644
index 0000000..6de56b5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.requests.LocatableEventRequestor;
+import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.requests.Requestor;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.xdebugger.impl.XDebugSessionImpl;
+import com.sun.jdi.InternalException;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.event.*;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.ThreadDeathRequest;
+import com.sun.jdi.request.ThreadStartRequest;
+
+/**
+ * @author lex
+ */
+public class DebugProcessEvents extends DebugProcessImpl {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessEvents");
+ private DebuggerEventThread myEventThread;
+ private final BreakpointManager myBreakpointManager;
+
+ public DebugProcessEvents(Project project) {
+ super(project);
+ myBreakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
+ }
+
+ protected void commitVM(final VirtualMachine vm) {
+ super.commitVM(vm);
+ if(vm != null) {
+ vmAttached();
+ myEventThread = new DebuggerEventThread();
+ ApplicationManager.getApplication().executeOnPooledThread(myEventThread);
+ }
+ }
+
+ private static void showStatusText(DebugProcessEvents debugProcess, Event event) {
+ Requestor requestor = debugProcess.getRequestsManager().findRequestor(event.request());
+ Breakpoint breakpoint = null;
+ if(requestor instanceof Breakpoint) {
+ breakpoint = (Breakpoint)requestor;
+ }
+ String text = debugProcess.getEventText(new Pair<Breakpoint, Event>(breakpoint, event));
+ debugProcess.showStatusText(text);
+ }
+
+ public String getEventText(Pair<Breakpoint, Event> descriptor) {
+ String text = "";
+ final Event event = descriptor.getSecond();
+ final Breakpoint breakpoint = descriptor.getFirst();
+ if (event instanceof LocatableEvent) {
+ if (breakpoint instanceof LineBreakpoint && !((LineBreakpoint)breakpoint).isVisible()) {
+ text = DebuggerBundle.message("status.stopped.at.cursor");
+ }
+ else {
+ try {
+ text = breakpoint != null? breakpoint.getEventMessage(((LocatableEvent)event)) : DebuggerBundle.message("status.generic.breakpoint.reached");
+ }
+ catch (InternalException e) {
+ text = DebuggerBundle.message("status.generic.breakpoint.reached");
+ }
+ }
+ }
+ else if (event instanceof VMStartEvent) {
+ text = DebuggerBundle.message("status.process.started");
+ }
+ else if (event instanceof VMDeathEvent) {
+ text = DebuggerBundle.message("status.process.terminated");
+ }
+ else if (event instanceof VMDisconnectEvent) {
+ final RemoteConnection connection = getConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
+ final String transportName = DebuggerBundle.getTransportName(connection);
+ text = DebuggerBundle.message("status.disconnected", addressDisplayName, transportName);
+ }
+ return text;
+ }
+
+ private class DebuggerEventThread implements Runnable {
+ private final VirtualMachineProxyImpl myVmProxy;
+
+ DebuggerEventThread () {
+ myVmProxy = getVirtualMachineProxy();
+ }
+
+ private boolean myIsStopped = false;
+
+ public synchronized void stopListening() {
+ myIsStopped = true;
+ }
+
+ private synchronized boolean isStopped() {
+ return myIsStopped;
+ }
+
+ public void run() {
+ try {
+ EventQueue eventQueue = myVmProxy.eventQueue();
+ while (!isStopped()) {
+ try {
+ final EventSet eventSet = eventQueue.remove();
+
+ final boolean methodWatcherActive = myReturnValueWatcher != null && myReturnValueWatcher.isEnabled();
+ int processed = 0;
+ for (EventIterator eventIterator = eventSet.eventIterator(); eventIterator.hasNext();) {
+ final Event event = eventIterator.nextEvent();
+
+ if (methodWatcherActive) {
+ if (event instanceof MethodExitEvent) {
+ if (myReturnValueWatcher.processMethodExitEvent((MethodExitEvent)event)) {
+ processed++;
+ }
+ continue;
+ }
+ }
+ if (event instanceof ThreadStartEvent) {
+ processed++;
+ final ThreadReference thread = ((ThreadStartEvent)event).thread();
+ getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ getVirtualMachineProxy().threadStarted(thread);
+ myDebugProcessDispatcher.getMulticaster().threadStarted(DebugProcessEvents.this, thread);
+ }
+ });
+ }
+ else if (event instanceof ThreadDeathEvent) {
+ processed++;
+ final ThreadReference thread = ((ThreadDeathEvent)event).thread();
+ getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ getVirtualMachineProxy().threadStopped(thread);
+ myDebugProcessDispatcher.getMulticaster().threadStopped(DebugProcessEvents.this, thread);
+ }
+ });
+ }
+ }
+
+ if (processed == eventSet.size()) {
+ eventSet.resume();
+ continue;
+ }
+
+ getManagerThread().invokeAndWait(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+
+ if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL && !enableBreakpointsDuringEvaluation()) {
+ // check if there is already one request with policy SUSPEND_ALL
+ for (SuspendContextImpl context : getSuspendManager().getEventContexts()) {
+ if (context.getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
+ eventSet.resume();
+ return;
+ }
+ }
+ }
+
+ final SuspendContextImpl suspendContext = getSuspendManager().pushSuspendContext(eventSet);
+
+ for (EventIterator eventIterator = eventSet.eventIterator(); eventIterator.hasNext();) {
+ final Event event = eventIterator.nextEvent();
+
+ //if (LOG.isDebugEnabled()) {
+ // LOG.debug("EVENT : " + event);
+ //}
+ try {
+ if (event instanceof VMStartEvent) {
+ //Sun WTK fails when J2ME when event set is resumed on VMStartEvent
+ processVMStartEvent(suspendContext, (VMStartEvent)event);
+ }
+ else if (event instanceof VMDeathEvent) {
+ processVMDeathEvent(suspendContext, event);
+ }
+ else if (event instanceof VMDisconnectEvent) {
+ processVMDeathEvent(suspendContext, event);
+ }
+ else if (event instanceof ClassPrepareEvent) {
+ processClassPrepareEvent(suspendContext, (ClassPrepareEvent)event);
+ }
+ //AccessWatchpointEvent, BreakpointEvent, ExceptionEvent, MethodEntryEvent, MethodExitEvent,
+ //ModificationWatchpointEvent, StepEvent, WatchpointEvent
+ else if (event instanceof StepEvent) {
+ processStepEvent(suspendContext, (StepEvent)event);
+ }
+ else if (event instanceof LocatableEvent) {
+ processLocatableEvent(suspendContext, (LocatableEvent)event);
+ }
+ else if (event instanceof ClassUnloadEvent) {
+ processDefaultEvent(suspendContext);
+ }
+ }
+ catch (VMDisconnectedException e) {
+ LOG.debug(e);
+ }
+ catch (InternalException e) {
+ LOG.info(e);
+ }
+ catch (Throwable e) {
+ LOG.error(e);
+ }
+ }
+ }
+ });
+
+ }
+ catch (InternalException e) {
+ LOG.debug(e);
+ }
+ catch (InterruptedException e) {
+ throw e;
+ }
+ catch (VMDisconnectedException e) {
+ throw e;
+ }
+ catch (ProcessCanceledException e) {
+ throw e;
+ }
+ catch (Throwable e) {
+ LOG.debug(e);
+ }
+ }
+ }
+ catch (InterruptedException e) {
+ invokeVMDeathEvent();
+ }
+ catch (VMDisconnectedException e) {
+ invokeVMDeathEvent();
+ } finally {
+ Thread.interrupted(); // reset interrupted status
+ }
+ }
+
+ private void invokeVMDeathEvent() {
+ getManagerThread().invokeAndWait(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ SuspendContextImpl suspendContext = getSuspendManager().pushSuspendContext(EventRequest.SUSPEND_NONE, 1);
+ processVMDeathEvent(suspendContext, null);
+ }
+ });
+ }
+ }
+
+ private static void preprocessEvent(SuspendContextImpl suspendContext, ThreadReference thread) {
+ ThreadReferenceProxyImpl oldThread = suspendContext.getThread();
+ suspendContext.setThread(thread);
+
+ if(oldThread == null) {
+ //this is the first event in the eventSet that we process
+ suspendContext.getDebugProcess().beforeSuspend(suspendContext);
+ }
+ }
+
+ private void processVMStartEvent(final SuspendContextImpl suspendContext, VMStartEvent event) {
+ preprocessEvent(suspendContext, event.thread());
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("enter: processVMStartEvent()");
+ }
+
+ showStatusText(this, event);
+
+ getSuspendManager().voteResume(suspendContext);
+ }
+
+ private void vmAttached() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ LOG.assertTrue(!isAttached());
+ if(myState.compareAndSet(STATE_INITIAL, STATE_ATTACHED)) {
+ final VirtualMachineProxyImpl machineProxy = getVirtualMachineProxy();
+ final EventRequestManager requestManager = machineProxy.eventRequestManager();
+
+ if (machineProxy.canGetMethodReturnValues()) {
+ myReturnValueWatcher = new MethodReturnValueWatcher(requestManager);
+ }
+
+ final ThreadStartRequest threadStartRequest = requestManager.createThreadStartRequest();
+ threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
+ threadStartRequest.enable();
+ final ThreadDeathRequest threadDeathRequest = requestManager.createThreadDeathRequest();
+ threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
+ threadDeathRequest.enable();
+
+ DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager().setInitialBreakpointsState();
+ myDebugProcessDispatcher.getMulticaster().processAttached(this);
+
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(getConnection());
+ final String transportName = DebuggerBundle.getTransportName(getConnection());
+ showStatusText(DebuggerBundle.message("status.connected", addressDisplayName, transportName));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("leave: processVMStartEvent()");
+ }
+ }
+ }
+
+ private void processVMDeathEvent(SuspendContextImpl suspendContext, Event event) {
+ try {
+ preprocessEvent(suspendContext, null);
+ cancelRunToCursorBreakpoint();
+ }
+ finally {
+ if (myEventThread != null) {
+ myEventThread.stopListening();
+ myEventThread = null;
+ }
+ closeProcess(false);
+ }
+
+ if(event != null) {
+ showStatusText(this, event);
+ }
+ }
+
+ private void processClassPrepareEvent(SuspendContextImpl suspendContext, ClassPrepareEvent event) {
+ preprocessEvent(suspendContext, event.thread());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Class prepared: " + event.referenceType().name());
+ }
+ suspendContext.getDebugProcess().getRequestsManager().processClassPrepared(event);
+
+ getSuspendManager().voteResume(suspendContext);
+ }
+
+ private void processStepEvent(SuspendContextImpl suspendContext, StepEvent event) {
+ final ThreadReference thread = event.thread();
+ //LOG.assertTrue(thread.isSuspended());
+ preprocessEvent(suspendContext, thread);
+
+ //noinspection HardCodedStringLiteral
+ RequestHint hint = (RequestHint)event.request().getProperty("hint");
+
+ deleteStepRequests(event.thread());
+
+ boolean shouldResume = false;
+
+ final Project project = getProject();
+ if (hint != null) {
+ final int nextStepDepth = hint.getNextStepDepth(suspendContext);
+ if (nextStepDepth != RequestHint.STOP) {
+ final ThreadReferenceProxyImpl threadProxy = suspendContext.getThread();
+ doStep(suspendContext, threadProxy, nextStepDepth, hint);
+ shouldResume = true;
+ }
+
+ if(!shouldResume && hint.isRestoreBreakpoints()) {
+ DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().enableBreakpoints(this);
+ }
+ }
+
+ if(shouldResume) {
+ getSuspendManager().voteResume(suspendContext);
+ }
+ else {
+ showStatusText("");
+ if (myReturnValueWatcher != null) {
+ myReturnValueWatcher.disable();
+ }
+ getSuspendManager().voteSuspend(suspendContext);
+ if (hint != null) {
+ final RequestHint.SmartStepFilter smartStepFilter = hint.getSmartStepFilter();
+ if (smartStepFilter != null && !smartStepFilter.wasMethodExecuted()) {
+ final String message = "Method <b>" + smartStepFilter.getTargetMethodName() + "()</b> has not been called";
+ XDebugSessionImpl.NOTIFICATION_GROUP.createNotification(message, MessageType.INFO).notify(project);
+ }
+ }
+ }
+ }
+
+ private void processLocatableEvent(final SuspendContextImpl suspendContext, final LocatableEvent event) {
+ if (myReturnValueWatcher != null && event instanceof MethodExitEvent) {
+ if (myReturnValueWatcher.processMethodExitEvent(((MethodExitEvent)event))) {
+ return;
+ }
+ }
+
+ ThreadReference thread = event.thread();
+ //LOG.assertTrue(thread.isSuspended());
+ preprocessEvent(suspendContext, thread);
+
+ //we use schedule to allow processing other events during processing this one
+ //this is especially nesessary if a method is breakpoint condition
+ getManagerThread().schedule(new SuspendContextCommandImpl(suspendContext) {
+ public void contextAction() throws Exception {
+ final SuspendManager suspendManager = getSuspendManager();
+ SuspendContextImpl evaluatingContext = SuspendManagerUtil.getEvaluatingContext(suspendManager, getSuspendContext().getThread());
+
+ if (evaluatingContext != null && !enableBreakpointsDuringEvaluation()) {
+ // is inside evaluation, so ignore any breakpoints
+ suspendManager.voteResume(suspendContext);
+ return;
+ }
+
+ final LocatableEventRequestor requestor = (LocatableEventRequestor) getRequestsManager().findRequestor(event.request());
+
+ boolean resumePreferred = requestor != null && DebuggerSettings.SUSPEND_NONE.equals(requestor.getSuspendPolicy());
+ boolean requestHit = false;
+ try {
+ requestHit = (requestor != null) && requestor.processLocatableEvent(this, event);
+ }
+ catch (final LocatableEventRequestor.EventProcessingException ex) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(ex.getMessage());
+ }
+ final boolean[] considerRequestHit = new boolean[]{true};
+ DebuggerInvocationUtil.invokeAndWait(getProject(), new Runnable() {
+ public void run() {
+ DebuggerPanelsManager.getInstance(getProject()).toFront(mySession);
+ final String displayName = requestor instanceof Breakpoint? ((Breakpoint)requestor).getDisplayName() : requestor.getClass().getSimpleName();
+ final String message = DebuggerBundle.message("error.evaluating.breakpoint.condition.or.action", displayName, ex.getMessage());
+ considerRequestHit[0] = Messages.showYesNoDialog(getProject(), message, ex.getTitle(), Messages.getQuestionIcon()) == 0;
+ }
+ }, ModalityState.NON_MODAL);
+ requestHit = considerRequestHit[0];
+ resumePreferred = !requestHit;
+ }
+
+ if (requestHit && requestor instanceof Breakpoint) {
+ // if requestor is a breakpoint and this breakpoint was hit, no matter its suspend policy
+ myBreakpointManager.processBreakpointHit((Breakpoint)requestor);
+ }
+
+ if(!requestHit || resumePreferred) {
+ suspendManager.voteResume(suspendContext);
+ }
+ else {
+ if (myReturnValueWatcher != null) {
+ myReturnValueWatcher.disable();
+ }
+ //if (suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
+ // // there could be explicit resume as a result of call to voteSuspend()
+ // // e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_
+ // // resuming and all breakpoints in other threads will be ignored.
+ // // As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens
+ // myBreakpointManager.applyThreadFilter(DebugProcessEvents.this, event.thread());
+ //}
+ suspendManager.voteSuspend(suspendContext);
+ showStatusText(DebugProcessEvents.this, event);
+ }
+ }
+ });
+ }
+
+ private static boolean enableBreakpointsDuringEvaluation() {
+ return Registry.is("debugger.enable.breakpoints.during.evaluation");
+ }
+
+ private void processDefaultEvent(SuspendContextImpl suspendContext) {
+ preprocessEvent(suspendContext, null);
+ getSuspendManager().voteResume(suspendContext);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java
new file mode 100644
index 0000000..3c71e0a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java
@@ -0,0 +1,1920 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.Patches;
+import com.intellij.debugger.*;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.apiAdapters.ConnectionServiceWrapper;
+import com.intellij.debugger.apiAdapters.TransportServiceWrapper;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
+import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.PrioritizedTask;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.RunToCursorBreakpoint;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.debugger.ui.tree.render.*;
+import com.intellij.execution.CantRunException;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessListener;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.runners.ExecutionUtil;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.ui.classFilter.DebuggerClassFilterProvider;
+import com.intellij.util.Alarm;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.concurrency.Semaphore;
+import com.sun.jdi.*;
+import com.sun.jdi.connect.*;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.StepRequest;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class DebugProcessImpl implements DebugProcess {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessImpl");
+
+ @NonNls private static final String SOCKET_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SocketAttach";
+ @NonNls private static final String SHMEM_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryAttach";
+ @NonNls private static final String SOCKET_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SocketListen";
+ @NonNls private static final String SHMEM_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryListen";
+
+ private final Project myProject;
+ private final RequestManagerImpl myRequestManager;
+
+ private VirtualMachineProxyImpl myVirtualMachineProxy = null;
+ protected EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class);
+ protected EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class);
+
+ private final List<ProcessListener> myProcessListeners = new ArrayList<ProcessListener>();
+
+ protected static final int STATE_INITIAL = 0;
+ protected static final int STATE_ATTACHED = 1;
+ protected static final int STATE_DETACHING = 2;
+ protected static final int STATE_DETACHED = 3;
+ protected final AtomicInteger myState = new AtomicInteger(STATE_INITIAL);
+
+ private ExecutionResult myExecutionResult;
+ private RemoteConnection myConnection;
+
+ private ConnectionServiceWrapper myConnectionService;
+ private Map<String, Connector.Argument> myArguments;
+
+ private final List<NodeRenderer> myRenderers = new ArrayList<NodeRenderer>();
+ private final Map<Type, NodeRenderer> myNodeRederersMap = new com.intellij.util.containers.HashMap<Type, NodeRenderer>();
+ private final NodeRendererSettingsListener mySettingsListener = new NodeRendererSettingsListener() {
+ public void renderersChanged() {
+ myNodeRederersMap.clear();
+ myRenderers.clear();
+ loadRenderers();
+ }
+ };
+
+ private final SuspendManagerImpl mySuspendManager = new SuspendManagerImpl(this);
+ protected CompoundPositionManager myPositionManager = null;
+ private volatile DebuggerManagerThreadImpl myDebuggerManagerThread;
+ private final HashMap myUserData = new HashMap();
+ private static final int LOCAL_START_TIMEOUT = 30000;
+
+ private final Semaphore myWaitFor = new Semaphore();
+ private final AtomicBoolean myBreakpointsMuted = new AtomicBoolean(false);
+ private boolean myIsFailed = false;
+ protected DebuggerSession mySession;
+ @Nullable protected MethodReturnValueWatcher myReturnValueWatcher;
+ private final Disposable myDisposable = Disposer.newDisposable();
+ private final Alarm myStatusUpdateAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myDisposable);
+
+ /** @noinspection FieldCanBeLocal*/
+ private volatile boolean myDebugProcessStarted = false;
+
+ protected DebugProcessImpl(Project project) {
+ myProject = project;
+ myRequestManager = new RequestManagerImpl(this);
+ NodeRendererSettings.getInstance().addListener(mySettingsListener);
+ loadRenderers();
+ }
+
+ private void loadRenderers() {
+ getManagerThread().invoke(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ try {
+ final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
+ for (final NodeRenderer renderer : rendererSettings.getAllRenderers()) {
+ if (renderer.isEnabled()) {
+ myRenderers.add(renderer);
+ }
+ }
+ }
+ finally {
+ DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
+ public void run() {
+ final DebuggerSession session = mySession;
+ if (session != null && session.isAttached()) {
+ session.refresh(true);
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+
+ @Nullable
+ public Pair<Method, Value> getLastExecutedMethod() {
+ final MethodReturnValueWatcher watcher = myReturnValueWatcher;
+ if (watcher == null) {
+ return null;
+ }
+ final Method method = watcher.getLastExecutedMethod();
+ if (method == null) {
+ return null;
+ }
+ return new Pair<Method, Value>(method, watcher.getLastMethodReturnValue());
+ }
+
+ public void setWatchMethodReturnValuesEnabled(boolean enabled) {
+ final MethodReturnValueWatcher watcher = myReturnValueWatcher;
+ if (watcher != null) {
+ watcher.setFeatureEnabled(enabled);
+ }
+ }
+
+ public boolean isWatchMethodReturnValuesEnabled() {
+ final MethodReturnValueWatcher watcher = myReturnValueWatcher;
+ return watcher != null && watcher.isFeatureEnabled();
+ }
+
+ public boolean canGetMethodReturnValue() {
+ return myReturnValueWatcher != null;
+ }
+
+ public NodeRenderer getAutoRenderer(ValueDescriptor descriptor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final Value value = descriptor.getValue();
+ Type type = value != null ? value.type() : null;
+
+ // in case evaluation is not possible, force default renderer
+ if (!DebuggerManagerEx.getInstanceEx(getProject()).getContext().isEvaluationPossible()) {
+ return getDefaultRenderer(type);
+ }
+
+ NodeRenderer renderer = myNodeRederersMap.get(type);
+ if(renderer == null) {
+ for (final NodeRenderer nodeRenderer : myRenderers) {
+ if (nodeRenderer.isApplicable(type)) {
+ renderer = nodeRenderer;
+ break;
+ }
+ }
+ if (renderer == null) {
+ renderer = getDefaultRenderer(type);
+ }
+ myNodeRederersMap.put(type, renderer);
+ }
+
+ return renderer;
+ }
+
+ public static NodeRenderer getDefaultRenderer(Value value) {
+ return getDefaultRenderer(value != null ? value.type() : null);
+ }
+
+ public static NodeRenderer getDefaultRenderer(Type type) {
+ final NodeRendererSettings settings = NodeRendererSettings.getInstance();
+
+ final PrimitiveRenderer primitiveRenderer = settings.getPrimitiveRenderer();
+ if(primitiveRenderer.isApplicable(type)) {
+ return primitiveRenderer;
+ }
+
+ final ArrayRenderer arrayRenderer = settings.getArrayRenderer();
+ if(arrayRenderer.isApplicable(type)) {
+ return arrayRenderer;
+ }
+
+ final ClassRenderer classRenderer = settings.getClassRenderer();
+ LOG.assertTrue(classRenderer.isApplicable(type), type.name());
+ return classRenderer;
+ }
+
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ protected void commitVM(VirtualMachine vm) {
+ if (!isInInitialState()) {
+ LOG.error("State is invalid " + myState.get());
+ }
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myPositionManager = createPositionManager();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("*******************VM attached******************");
+ }
+ checkVirtualMachineVersion(vm);
+
+ myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm);
+
+ String trace = System.getProperty("idea.debugger.trace");
+ if (trace != null) {
+ int mask = 0;
+ StringTokenizer tokenizer = new StringTokenizer(trace);
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ if ("SENDS".compareToIgnoreCase(token) == 0) {
+ mask |= VirtualMachine.TRACE_SENDS;
+ }
+ else if ("RAW_SENDS".compareToIgnoreCase(token) == 0) {
+ mask |= 0x01000000;
+ }
+ else if ("RECEIVES".compareToIgnoreCase(token) == 0) {
+ mask |= VirtualMachine.TRACE_RECEIVES;
+ }
+ else if ("RAW_RECEIVES".compareToIgnoreCase(token) == 0) {
+ mask |= 0x02000000;
+ }
+ else if ("EVENTS".compareToIgnoreCase(token) == 0) {
+ mask |= VirtualMachine.TRACE_EVENTS;
+ }
+ else if ("REFTYPES".compareToIgnoreCase(token) == 0) {
+ mask |= VirtualMachine.TRACE_REFTYPES;
+ }
+ else if ("OBJREFS".compareToIgnoreCase(token) == 0) {
+ mask |= VirtualMachine.TRACE_OBJREFS;
+ }
+ else if ("ALL".compareToIgnoreCase(token) == 0) {
+ mask |= VirtualMachine.TRACE_ALL;
+ }
+ }
+
+ vm.setDebugTraceMode(mask);
+ }
+ }
+
+ private void stopConnecting() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ Map arguments = myArguments;
+ try {
+ if (arguments == null) {
+ return;
+ }
+ if(myConnection.isServerMode()) {
+ ListeningConnector connector = (ListeningConnector)findConnector(SOCKET_LISTENING_CONNECTOR_NAME);
+ if (connector == null) {
+ LOG.error("Cannot find connector: " + SOCKET_LISTENING_CONNECTOR_NAME);
+ }
+ connector.stopListening(arguments);
+ }
+ else {
+ if(myConnectionService != null) {
+ myConnectionService.close();
+ }
+ }
+ }
+ catch (IOException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ }
+ catch (IllegalConnectorArgumentsException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ }
+ catch (ExecutionException e) {
+ LOG.error(e);
+ }
+ finally {
+ closeProcess(true);
+ }
+ }
+
+ protected CompoundPositionManager createPositionManager() {
+ return new CompoundPositionManager(new PositionManagerImpl(this));
+ }
+
+ public void printToConsole(final String text) {
+ myExecutionResult.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM);
+ }
+
+ /**
+ *
+ * @param suspendContext
+ * @param stepThread
+ * @param depth
+ * @param hint may be null
+ */
+ protected void doStep(final SuspendContextImpl suspendContext, final ThreadReferenceProxyImpl stepThread, int depth, RequestHint hint) {
+ if (stepThread == null) {
+ return;
+ }
+ try {
+ final ThreadReference stepThreadReference = stepThread.getThreadReference();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("DO_STEP: creating step request for " + stepThreadReference);
+ }
+ deleteStepRequests(stepThreadReference);
+ EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
+ StepRequest stepRequest = requestManager.createStepRequest(stepThreadReference, StepRequest.STEP_LINE, depth);
+ DebuggerSettings settings = DebuggerSettings.getInstance();
+ if (!(hint != null && hint.isIgnoreFilters()) /*&& depth == StepRequest.STEP_INTO*/) {
+ final List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
+ if (settings.TRACING_FILTERS_ENABLED) {
+ for (ClassFilter filter : settings.getSteppingFilters()) {
+ if (filter.isEnabled()) {
+ activeFilters.add(filter);
+ }
+ }
+ }
+ for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) {
+ for (ClassFilter filter : provider.getFilters()) {
+ if (filter.isEnabled()) {
+ activeFilters.add(filter);
+ }
+ }
+ }
+
+ if (!activeFilters.isEmpty()) {
+ final String currentClassName = getCurrentClassName(stepThread);
+ if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) {
+ // add class filters
+ for (ClassFilter filter : activeFilters) {
+ stepRequest.addClassExclusionFilter(filter.getPattern());
+ }
+ }
+ }
+ }
+
+ // suspend policy to match the suspend policy of the context:
+ // if all threads were suspended, then during stepping all the threads must be suspended
+ // if only event thread were suspended, then only this particular thread must be suspended during stepping
+ stepRequest.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? EventRequest.SUSPEND_EVENT_THREAD : EventRequest.SUSPEND_ALL);
+
+ if (hint != null) {
+ //noinspection HardCodedStringLiteral
+ stepRequest.putProperty("hint", hint);
+ }
+ stepRequest.enable();
+ }
+ catch (ObjectCollectedException ignored) {
+
+ }
+ }
+
+ void deleteStepRequests(@Nullable final ThreadReference stepThread) {
+ EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
+ List<StepRequest> stepRequests = requestManager.stepRequests();
+ if (!stepRequests.isEmpty()) {
+ final List<StepRequest> toDelete = new ArrayList<StepRequest>(stepRequests.size());
+ for (final StepRequest request : stepRequests) {
+ ThreadReference threadReference = request.thread();
+ // [jeka] on attempt to delete a request assigned to a thread with unknown status, a JDWP error occures
+ if (threadReference.status() != ThreadReference.THREAD_STATUS_UNKNOWN && (stepThread == null || stepThread.equals(threadReference))) {
+ toDelete.add(request);
+ }
+ }
+ requestManager.deleteEventRequests(toDelete);
+ }
+ }
+
+ @Nullable
+ private static String getCurrentClassName(ThreadReferenceProxyImpl thread) {
+ try {
+ if (thread != null && thread.frameCount() > 0) {
+ StackFrameProxyImpl stackFrame = thread.frame(0);
+ if (stackFrame != null) {
+ Location location = stackFrame.location();
+ ReferenceType referenceType = location.declaringType();
+ if (referenceType != null) {
+ return referenceType.name();
+ }
+ }
+ }
+ }
+ catch (EvaluateException ignored) {
+ }
+ return null;
+ }
+
+ private VirtualMachine createVirtualMachineInt() throws ExecutionException {
+ try {
+ if (myArguments != null) {
+ throw new IOException(DebuggerBundle.message("error.debugger.already.listening"));
+ }
+
+ final String address = myConnection.getAddress();
+ if (myConnection.isServerMode()) {
+ ListeningConnector connector = (ListeningConnector)findConnector(
+ myConnection.isUseSockets() ? SOCKET_LISTENING_CONNECTOR_NAME : SHMEM_LISTENING_CONNECTOR_NAME);
+ if (connector == null) {
+ throw new CantRunException(DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
+ }
+ myArguments = connector.defaultArguments();
+ if (myArguments == null) {
+ throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
+ }
+
+ if (address == null) {
+ throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
+ }
+ // negative port number means the caller leaves to debugger to decide at which hport to listen
+ //noinspection HardCodedStringLiteral
+ final Connector.Argument portArg = myConnection.isUseSockets() ? myArguments.get("port") : myArguments.get("name");
+ if (portArg != null) {
+ portArg.setValue(address);
+ }
+ //noinspection HardCodedStringLiteral
+ final Connector.Argument timeoutArg = myArguments.get("timeout");
+ if (timeoutArg != null) {
+ timeoutArg.setValue("0"); // wait forever
+ }
+ connector.startListening(myArguments);
+ myDebugProcessDispatcher.getMulticaster().connectorIsReady();
+ try {
+ return connector.accept(myArguments);
+ }
+ finally {
+ if(myArguments != null) {
+ try {
+ connector.stopListening(myArguments);
+ }
+ catch (IllegalArgumentException e) {
+ // ignored
+ }
+ catch (IllegalConnectorArgumentsException e) {
+ // ignored
+ }
+ }
+ }
+ }
+ else { // is client mode, should attach to already running process
+ AttachingConnector connector = (AttachingConnector)findConnector(
+ myConnection.isUseSockets() ? SOCKET_ATTACHING_CONNECTOR_NAME : SHMEM_ATTACHING_CONNECTOR_NAME
+ );
+
+ if (connector == null) {
+ throw new CantRunException( DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
+ }
+ myArguments = connector.defaultArguments();
+ if (myConnection.isUseSockets()) {
+ //noinspection HardCodedStringLiteral
+ final Connector.Argument hostnameArg = myArguments.get("hostname");
+ if (hostnameArg != null && myConnection.getHostName() != null) {
+ hostnameArg.setValue(myConnection.getHostName());
+ }
+ if (address == null) {
+ throw new CantRunException(DebuggerBundle.message("error.no.debug.attach.port"));
+ }
+ //noinspection HardCodedStringLiteral
+ final Connector.Argument portArg = myArguments.get("port");
+ if (portArg != null) {
+ portArg.setValue(address);
+ }
+ }
+ else {
+ if (address == null) {
+ throw new CantRunException(DebuggerBundle.message("error.no.shmem.address"));
+ }
+ //noinspection HardCodedStringLiteral
+ final Connector.Argument nameArg = myArguments.get("name");
+ if (nameArg != null) {
+ nameArg.setValue(address);
+ }
+ }
+ //noinspection HardCodedStringLiteral
+ final Connector.Argument timeoutArg = myArguments.get("timeout");
+ if (timeoutArg != null) {
+ timeoutArg.setValue("0"); // wait forever
+ }
+
+ myDebugProcessDispatcher.getMulticaster().connectorIsReady();
+ try {
+ if (SOCKET_ATTACHING_CONNECTOR_NAME.equals(connector.name()) && Patches.SUN_JDI_CONNECTOR_HANGUP_BUG) {
+ String portString = myConnection.getAddress();
+ String hostString = myConnection.getHostName();
+
+ if (hostString == null || hostString.isEmpty()) {
+ //noinspection HardCodedStringLiteral
+ hostString = "localhost";
+ }
+ hostString += ":";
+
+ final TransportServiceWrapper transportServiceWrapper = TransportServiceWrapper.getTransportService(connector.transport());
+ myConnectionService = transportServiceWrapper.attach(hostString + portString);
+ return myConnectionService.createVirtualMachine();
+ }
+ else {
+ return connector.attach(myArguments);
+ }
+ }
+ catch (IllegalArgumentException e) {
+ throw new CantRunException(e.getLocalizedMessage());
+ }
+ }
+ }
+ catch (IOException e) {
+ throw new ExecutionException(processError(e), e);
+ }
+ catch (IllegalConnectorArgumentsException e) {
+ throw new ExecutionException(processError(e), e);
+ }
+ finally {
+ myArguments = null;
+ myConnectionService = null;
+ }
+ }
+
+ public void showStatusText(final String text) {
+ if (!myStatusUpdateAlarm.isDisposed()) {
+ myStatusUpdateAlarm.cancelAllRequests();
+ myStatusUpdateAlarm.addRequest(new Runnable() {
+ public void run() {
+ final WindowManager wm = WindowManager.getInstance();
+ if (wm != null) {
+ wm.getStatusBar(myProject).setInfo(text);
+ }
+ }
+ }, 50);
+ }
+ }
+
+ static Connector findConnector(String connectorName) throws ExecutionException {
+ VirtualMachineManager virtualMachineManager;
+ try {
+ virtualMachineManager = Bootstrap.virtualMachineManager();
+ }
+ catch (Error e) {
+ final String error = e.getClass().getName() + " : " + e.getLocalizedMessage();
+ throw new ExecutionException(DebuggerBundle.message("debugger.jdi.bootstrap.error", error));
+ }
+ List connectors;
+ if (SOCKET_ATTACHING_CONNECTOR_NAME.equals(connectorName) || SHMEM_ATTACHING_CONNECTOR_NAME.equals(connectorName)) {
+ connectors = virtualMachineManager.attachingConnectors();
+ }
+ else if (SOCKET_LISTENING_CONNECTOR_NAME.equals(connectorName) || SHMEM_LISTENING_CONNECTOR_NAME.equals(connectorName)) {
+ connectors = virtualMachineManager.listeningConnectors();
+ }
+ else {
+ return null;
+ }
+ for (Object connector1 : connectors) {
+ Connector connector = (Connector)connector1;
+ if (connectorName.equals(connector.name())) {
+ return connector;
+ }
+ }
+ return null;
+ }
+
+ private void checkVirtualMachineVersion(VirtualMachine vm) {
+ final String version = vm.version();
+ if ("1.4.0".equals(version)) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ Messages.showMessageDialog(
+ getProject(),
+ DebuggerBundle.message("warning.jdk140.unstable"), DebuggerBundle.message("title.jdk140.unstable"), Messages.getWarningIcon()
+ );
+ }
+ });
+ }
+ }
+
+ /*Event dispatching*/
+ public void addEvaluationListener(EvaluationListener evaluationListener) {
+ myEvaluationDispatcher.addListener(evaluationListener);
+ }
+
+ public void removeEvaluationListener(EvaluationListener evaluationListener) {
+ myEvaluationDispatcher.removeListener(evaluationListener);
+ }
+
+ public void addDebugProcessListener(DebugProcessListener listener) {
+ myDebugProcessDispatcher.addListener(listener);
+ }
+
+ public void removeDebugProcessListener(DebugProcessListener listener) {
+ myDebugProcessDispatcher.removeListener(listener);
+ }
+
+ public void addProcessListener(ProcessListener processListener) {
+ synchronized(myProcessListeners) {
+ if(getExecutionResult() != null) {
+ getExecutionResult().getProcessHandler().addProcessListener(processListener);
+ }
+ else {
+ myProcessListeners.add(processListener);
+ }
+ }
+ }
+
+ public void removeProcessListener(ProcessListener processListener) {
+ synchronized (myProcessListeners) {
+ if(getExecutionResult() != null) {
+ getExecutionResult().getProcessHandler().removeProcessListener(processListener);
+ }
+ else {
+ myProcessListeners.remove(processListener);
+ }
+ }
+ }
+
+ /* getters */
+ public RemoteConnection getConnection() {
+ return myConnection;
+ }
+
+ public ExecutionResult getExecutionResult() {
+ return myExecutionResult;
+ }
+
+ public <T> T getUserData(Key<T> key) {
+ return (T)myUserData.get(key);
+ }
+
+ public <T> void putUserData(Key<T> key, T value) {
+ myUserData.put(key, value);
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ public boolean canRedefineClasses() {
+ return myVirtualMachineProxy != null && myVirtualMachineProxy.canRedefineClasses();
+ }
+
+ public boolean canWatchFieldModification() {
+ return myVirtualMachineProxy != null && myVirtualMachineProxy.canWatchFieldModification();
+ }
+
+ public boolean isInInitialState() {
+ return myState.get() == STATE_INITIAL;
+ }
+
+ public boolean isAttached() {
+ return myState.get() == STATE_ATTACHED;
+ }
+
+ public boolean isDetached() {
+ return myState.get() == STATE_DETACHED;
+ }
+
+ public boolean isDetaching() {
+ return myState.get() == STATE_DETACHING;
+ }
+
+ public RequestManagerImpl getRequestsManager() {
+ return myRequestManager;
+ }
+
+ public VirtualMachineProxyImpl getVirtualMachineProxy() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (myVirtualMachineProxy == null) {
+ throw new VMDisconnectedException();
+ }
+ return myVirtualMachineProxy;
+ }
+
+ public void appendPositionManager(final PositionManager positionManager) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myPositionManager.appendPositionManager(positionManager);
+ DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().updateBreakpoints(this);
+ }
+
+ private RunToCursorBreakpoint myRunToCursorBreakpoint;
+
+ public void cancelRunToCursorBreakpoint() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (myRunToCursorBreakpoint != null) {
+ getRequestsManager().deleteRequest(myRunToCursorBreakpoint);
+ myRunToCursorBreakpoint.delete();
+ if (myRunToCursorBreakpoint.isRestoreBreakpoints()) {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
+ breakpointManager.enableBreakpoints(this);
+ }
+ myRunToCursorBreakpoint = null;
+ }
+ }
+
+ protected void closeProcess(boolean closedByUser) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ if (myState.compareAndSet(STATE_INITIAL, STATE_DETACHING) || myState.compareAndSet(STATE_ATTACHED, STATE_DETACHING)) {
+ try {
+ getManagerThread().close();
+ }
+ finally {
+ myVirtualMachineProxy = null;
+ myPositionManager = null;
+ myReturnValueWatcher = null;
+ myNodeRederersMap.clear();
+ myRenderers.clear();
+ DebuggerUtils.cleanupAfterProcessFinish(this);
+ myState.set(STATE_DETACHED);
+ try {
+ myDebugProcessDispatcher.getMulticaster().processDetached(this, closedByUser);
+ }
+ finally {
+ setBreakpointsMuted(false);
+ myWaitFor.up();
+ }
+ }
+
+ }
+ }
+
+ private static String formatMessage(String message) {
+ final int lineLength = 90;
+ StringBuilder buf = new StringBuilder(message.length());
+ int index = 0;
+ while (index < message.length()) {
+ buf.append(message.substring(index, Math.min(index + lineLength, message.length()))).append('\n');
+ index += lineLength;
+ }
+ return buf.toString();
+ }
+
+ public static String processError(Exception e) {
+ String message;
+
+ if (e instanceof VMStartException) {
+ VMStartException e1 = (VMStartException)e;
+ message = e1.getLocalizedMessage();
+ }
+ else if (e instanceof IllegalConnectorArgumentsException) {
+ IllegalConnectorArgumentsException e1 = (IllegalConnectorArgumentsException)e;
+ final List<String> invalidArgumentNames = e1.argumentNames();
+ message = formatMessage(DebuggerBundle.message("error.invalid.argument", invalidArgumentNames.size()) + ": "+ e1.getLocalizedMessage()) + invalidArgumentNames;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e1);
+ }
+ }
+ else if (e instanceof CantRunException) {
+ message = e.getLocalizedMessage();
+ }
+ else if (e instanceof VMDisconnectedException) {
+ message = DebuggerBundle.message("error.vm.disconnected");
+ }
+ else if (e instanceof UnknownHostException) {
+ message = DebuggerBundle.message("error.unknown.host") + ":\n" + e.getLocalizedMessage();
+ }
+ else if (e instanceof IOException) {
+ IOException e1 = (IOException)e;
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ buf.append(DebuggerBundle.message("error.cannot.open.debugger.port")).append(" : ");
+ buf.append(e1.getClass().getName()).append(" ");
+ final String localizedMessage = e1.getLocalizedMessage();
+ if (localizedMessage != null && !localizedMessage.isEmpty()) {
+ buf.append('"');
+ buf.append(localizedMessage);
+ buf.append('"');
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e1);
+ }
+ message = buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+ else if (e instanceof ExecutionException) {
+ message = e.getLocalizedMessage();
+ }
+ else {
+ message = DebuggerBundle.message("error.exception.while.connecting", e.getClass().getName(), e.getLocalizedMessage());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ }
+ return message;
+ }
+
+ public void dispose() {
+ NodeRendererSettings.getInstance().removeListener(mySettingsListener);
+ Disposer.dispose(myDisposable);
+ }
+
+ public DebuggerManagerThreadImpl getManagerThread() {
+ if (myDebuggerManagerThread == null) {
+ synchronized (this) {
+ if (myDebuggerManagerThread == null) {
+ myDebuggerManagerThread = new DebuggerManagerThreadImpl(myDisposable);
+ }
+ }
+ }
+ return myDebuggerManagerThread;
+ }
+
+ private static int getInvokePolicy(SuspendContext suspendContext) {
+ //return ThreadReference.INVOKE_SINGLE_THREADED;
+ return suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? ObjectReference.INVOKE_SINGLE_THREADED : 0;
+ }
+
+ public void waitFor() {
+ LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
+ myWaitFor.waitFor();
+ }
+
+ public void waitFor(long timeout) {
+ LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
+ myWaitFor.waitFor(timeout);
+ }
+
+ private abstract class InvokeCommand <E extends Value> {
+ private final List myArgs;
+
+ protected InvokeCommand(List args) {
+ if (!args.isEmpty()) {
+ myArgs = new ArrayList(args);
+ }
+ else {
+ myArgs = args;
+ }
+ }
+
+ public String toString() {
+ return "INVOKE: " + super.toString();
+ }
+
+ protected abstract E invokeMethod(int invokePolicy, final List args) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException;
+
+ public E start(EvaluationContextImpl evaluationContext, Method method) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ SuspendContextImpl suspendContext = evaluationContext.getSuspendContext();
+ SuspendManagerUtil.assertSuspendContext(suspendContext);
+
+ ThreadReferenceProxyImpl invokeThread = suspendContext.getThread();
+
+ if (SuspendManagerUtil.isEvaluating(getSuspendManager(), invokeThread)) {
+ throw EvaluateExceptionUtil.NESTED_EVALUATION_ERROR;
+ }
+
+ Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), invokeThread);
+ final ThreadReference invokeThreadRef = invokeThread.getThreadReference();
+
+ myEvaluationDispatcher.getMulticaster().evaluationStarted(suspendContext);
+ beforeMethodInvocation(suspendContext, method);
+
+ Object resumeData = null;
+ try {
+ for (final SuspendContextImpl suspendingContext : suspendingContexts) {
+ final ThreadReferenceProxyImpl suspendContextThread = suspendingContext.getThread();
+ if (suspendContextThread != invokeThread) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Resuming " + invokeThread + " that is paused by " + suspendContextThread);
+ }
+ LOG.assertTrue(suspendContextThread == null || !invokeThreadRef.equals(suspendContextThread.getThreadReference()));
+ getSuspendManager().resumeThread(suspendingContext, invokeThread);
+ }
+ }
+
+ resumeData = SuspendManagerUtil.prepareForResume(suspendContext);
+ suspendContext.setIsEvaluating(evaluationContext);
+
+ getVirtualMachineProxy().clearCaches();
+
+ while (true) {
+ try {
+ return invokeMethodAndFork(suspendContext);
+ }
+ catch (ClassNotLoadedException e) {
+ ReferenceType loadedClass;
+ try {
+ loadedClass = evaluationContext.isAutoLoadClasses()? loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader()) : null;
+ }
+ catch (EvaluateException ignored) {
+ loadedClass = null;
+ }
+ if (loadedClass == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+ }
+ }
+ catch (ClassNotLoadedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InvocationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InvalidTypeException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (ObjectCollectedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (UnsupportedOperationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InternalException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ finally {
+ suspendContext.setIsEvaluating(null);
+ if (resumeData != null) {
+ SuspendManagerUtil.restoreAfterResume(suspendContext, resumeData);
+ }
+ for (SuspendContextImpl suspendingContext : mySuspendManager.getEventContexts()) {
+ if (suspendingContexts.contains(suspendingContext) && !suspendingContext.isEvaluating() && !suspendingContext.suspends(invokeThread)) {
+ mySuspendManager.suspendThread(suspendingContext, invokeThread);
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("getVirtualMachine().clearCaches()");
+ }
+ getVirtualMachineProxy().clearCaches();
+ afterMethodInvocation(suspendContext);
+
+ myEvaluationDispatcher.getMulticaster().evaluationFinished(suspendContext);
+ }
+ }
+
+ private E invokeMethodAndFork(final SuspendContextImpl context) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException {
+ final int invokePolicy = getInvokePolicy(context);
+ final Exception[] exception = new Exception[1];
+ final Value[] result = new Value[1];
+ getManagerThread().startLongProcessAndFork(new Runnable() {
+ public void run() {
+ ThreadReferenceProxyImpl thread = context.getThread();
+ try {
+ try {
+ if (LOG.isDebugEnabled()) {
+ final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
+ virtualMachineProxy.logThreads();
+ LOG.debug("Invoke in " + thread.name());
+ assertThreadSuspended(thread, context);
+ }
+ if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
+ // ensure args are not collected
+ for (Object arg : myArgs) {
+ if (arg instanceof ObjectReference) {
+ ((ObjectReference)arg).disableCollection();
+ }
+ }
+ }
+ result[0] = invokeMethod(invokePolicy, myArgs);
+ }
+ finally {
+ // assertThreadSuspended(thread, context);
+ if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
+ // ensure args are not collected
+ for (Object arg : myArgs) {
+ if (arg instanceof ObjectReference) {
+ ((ObjectReference)arg).enableCollection();
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ exception[0] = e;
+ }
+ }
+ });
+
+ if (exception[0] != null) {
+ if (exception[0] instanceof InvocationException) {
+ throw (InvocationException)exception[0];
+ }
+ else if (exception[0] instanceof ClassNotLoadedException) {
+ throw (ClassNotLoadedException)exception[0];
+ }
+ else if (exception[0] instanceof IncompatibleThreadStateException) {
+ throw (IncompatibleThreadStateException)exception[0];
+ }
+ else if (exception[0] instanceof InvalidTypeException) {
+ throw (InvalidTypeException)exception[0];
+ }
+ else if (exception[0] instanceof RuntimeException) {
+ throw (RuntimeException)exception[0];
+ }
+ else {
+ LOG.assertTrue(false);
+ }
+ }
+
+ return (E)result[0];
+ }
+
+ private void assertThreadSuspended(final ThreadReferenceProxyImpl thread, final SuspendContextImpl context) {
+ LOG.assertTrue(context.isEvaluating());
+ try {
+ final boolean isSuspended = thread.isSuspended();
+ LOG.assertTrue(isSuspended, thread);
+ }
+ catch (ObjectCollectedException ignored) {
+ }
+ }
+ }
+
+ public Value invokeMethod(final EvaluationContext evaluationContext, final ObjectReference objRef, final Method method, final List args) throws EvaluateException {
+ return invokeInstanceMethod(evaluationContext, objRef, method, args, 0);
+ }
+
+ public Value invokeInstanceMethod(final EvaluationContext evaluationContext, final ObjectReference objRef, final Method method,
+ final List args, final int invocationOptions) throws EvaluateException {
+ final ThreadReference thread = getEvaluationThread(evaluationContext);
+ return new InvokeCommand<Value>(args) {
+ protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Invoke " + method.name());
+ }
+ return objRef.invokeMethod(thread, method, args, invokePolicy | invocationOptions);
+ }
+ }.start((EvaluationContextImpl)evaluationContext, method);
+ }
+
+ private static ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException {
+ ThreadReferenceProxy evaluationThread = evaluationContext.getSuspendContext().getThread();
+ if(evaluationThread == null) {
+ throw EvaluateExceptionUtil.NULL_STACK_FRAME;
+ }
+ return evaluationThread.getThreadReference();
+ }
+
+ public Value invokeMethod(final EvaluationContext evaluationContext, final ClassType classType,
+ final Method method,
+ final List args) throws EvaluateException {
+
+ final ThreadReference thread = getEvaluationThread(evaluationContext);
+ InvokeCommand<Value> invokeCommand = new InvokeCommand<Value>(args) {
+ protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Invoke " + method.name());
+ }
+ return classType.invokeMethod(thread, method, args, invokePolicy);
+ }
+ };
+ return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
+ }
+
+ public ArrayReference newInstance(final ArrayType arrayType,
+ final int dimension)
+ throws EvaluateException {
+ return arrayType.newInstance(dimension);
+ }
+
+ public ObjectReference newInstance(final EvaluationContext evaluationContext, final ClassType classType,
+ final Method method,
+ final List args) throws EvaluateException {
+ final ThreadReference thread = getEvaluationThread(evaluationContext);
+ InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<ObjectReference>(args) {
+ protected ObjectReference invokeMethod(int invokePolicy, final List args) throws InvocationException,
+ ClassNotLoadedException,
+ IncompatibleThreadStateException,
+ InvalidTypeException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("New instance " + method.name());
+ }
+ return classType.newInstance(thread, method, args, invokePolicy);
+ }
+ };
+ return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
+ }
+
+ public void clearCashes(int suspendPolicy) {
+ if (!isAttached()) return;
+ switch (suspendPolicy) {
+ case EventRequest.SUSPEND_ALL:
+ getVirtualMachineProxy().clearCaches();
+ break;
+ case EventRequest.SUSPEND_EVENT_THREAD:
+ getVirtualMachineProxy().clearCaches();
+ //suspendContext.getThread().clearAll();
+ break;
+ }
+ }
+
+ protected void beforeSuspend(SuspendContextImpl suspendContext) {
+ clearCashes(suspendContext.getSuspendPolicy());
+ }
+
+ private void beforeMethodInvocation(SuspendContextImpl suspendContext, Method method) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "before invocation in thread " + suspendContext.getThread().name() + " method " + (method == null ? "null" : method.name()));
+ }
+
+ if (method != null) {
+ showStatusText(DebuggerBundle.message("progress.evaluating", DebuggerUtilsEx.methodName(method)));
+ }
+ else {
+ showStatusText(DebuggerBundle.message("title.evaluating"));
+ }
+ }
+
+ private void afterMethodInvocation(SuspendContextImpl suspendContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("after invocation in thread " + suspendContext.getThread().name());
+ }
+ showStatusText("");
+ }
+
+ public ReferenceType findClass(EvaluationContext evaluationContext, String className,
+ ClassLoaderReference classLoader) throws EvaluateException {
+ try {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final VirtualMachineProxyImpl vmProxy = getVirtualMachineProxy();
+ if (vmProxy == null) {
+ throw new VMDisconnectedException();
+ }
+ ReferenceType result = null;
+ for (final ReferenceType refType : vmProxy.classesByName(className)) {
+ if (refType.isPrepared() && isVisibleFromClassLoader(classLoader, refType)) {
+ result = refType;
+ break;
+ }
+ }
+ final EvaluationContextImpl evalContext = (EvaluationContextImpl)evaluationContext;
+ if (result == null && evalContext.isAutoLoadClasses()) {
+ return loadClass(evalContext, className, classLoader);
+ }
+ return result;
+ }
+ catch (InvocationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (ClassNotLoadedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InvalidTypeException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ private static boolean isVisibleFromClassLoader(final ClassLoaderReference fromLoader, final ReferenceType refType) {
+ // IMPORTANT! Even if the refType is already loaded by some parent or bootstrap loader, it may not be visible from the given loader.
+ // For example because there were no accesses yet from this loader to this class. So the loader is not in the list of "initialing" loaders
+ // for this refType and the refType is not visible to the loader.
+ // Attempt to evaluate method with this refType will yield ClassNotLoadedException.
+ // The only way to say for sure whether the class is _visible_ to the given loader, is to use the following API call
+ return fromLoader == null || fromLoader.visibleClasses().contains(refType);
+ }
+
+ private static String reformatArrayName(String className) {
+ if (className.indexOf('[') == -1) return className;
+
+ int dims = 0;
+ while (className.endsWith("[]")) {
+ className = className.substring(0, className.length() - 2);
+ dims++;
+ }
+
+ StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ for (int i = 0; i < dims; i++) {
+ buffer.append('[');
+ }
+ String primitiveSignature = JVMNameUtil.getPrimitiveSignature(className);
+ if(primitiveSignature != null) {
+ buffer.append(primitiveSignature);
+ }
+ else {
+ buffer.append('L');
+ buffer.append(className);
+ buffer.append(';');
+ }
+ return buffer.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String qName, ClassLoaderReference classLoader)
+ throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, EvaluateException {
+
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ qName = reformatArrayName(qName);
+ ReferenceType refType = null;
+ VirtualMachineProxyImpl virtualMachine = getVirtualMachineProxy();
+ final List classClasses = virtualMachine.classesByName("java.lang.Class");
+ if (!classClasses.isEmpty()) {
+ ClassType classClassType = (ClassType)classClasses.get(0);
+ final Method forNameMethod;
+ if (classLoader != null) {
+ //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ }
+ else {
+ //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ }
+ final List<Mirror> args = new ArrayList<Mirror>(); // do not use unmodifiable lists because the list is modified by JPDA
+ final StringReference qNameMirror = virtualMachine.mirrorOf(qName);
+ args.add(qNameMirror);
+ if (classLoader != null) {
+ args.add(virtualMachine.mirrorOf(true));
+ args.add(classLoader);
+ }
+ final Value value = invokeMethod(evaluationContext, classClassType, forNameMethod, args);
+ if (value instanceof ClassObjectReference) {
+ refType = ((ClassObjectReference)value).reflectedType();
+ }
+ }
+ return refType;
+ }
+
+ public void logThreads() {
+ if (LOG.isDebugEnabled()) {
+ try {
+ Collection<ThreadReferenceProxyImpl> allThreads = getVirtualMachineProxy().allThreads();
+ for (ThreadReferenceProxyImpl threadReferenceProxy : allThreads) {
+ LOG.debug("Thread name=" + threadReferenceProxy.name() + " suspendCount()=" + threadReferenceProxy.getSuspendCount());
+ }
+ }
+ catch (Exception e) {
+ LOG.debug(e);
+ }
+ }
+ }
+
+ public SuspendManager getSuspendManager() {
+ return mySuspendManager;
+ }
+
+ public CompoundPositionManager getPositionManager() {
+ return myPositionManager;
+ }
+ //ManagerCommands
+
+ public void stop(boolean forceTerminate) {
+ this.getManagerThread().terminateAndInvoke(createStopCommand(forceTerminate), DebuggerManagerThreadImpl.COMMAND_TIMEOUT);
+ }
+
+ public StopCommand createStopCommand(boolean forceTerminate) {
+ return new StopCommand(forceTerminate);
+ }
+
+ protected class StopCommand extends DebuggerCommandImpl {
+ private final boolean myIsTerminateTargetVM;
+
+ public StopCommand(boolean isTerminateTargetVM) {
+ myIsTerminateTargetVM = isTerminateTargetVM;
+ }
+
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+
+ protected void action() throws Exception {
+ if (isAttached()) {
+ final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
+ if (myIsTerminateTargetVM) {
+ virtualMachineProxy.exit(-1);
+ }
+ else {
+ // some VM's (like IBM VM 1.4.2 bundled with WebSpere) does not
+ // resume threads on dispose() like it should
+ virtualMachineProxy.resume();
+ virtualMachineProxy.dispose();
+ }
+ }
+ else {
+ stopConnecting();
+ }
+ }
+ }
+
+ private class StepOutCommand extends ResumeCommand {
+ public StepOutCommand(SuspendContextImpl suspendContext) {
+ super(suspendContext);
+ }
+
+ public void contextAction() {
+ showStatusText(DebuggerBundle.message("status.step.out"));
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ final ThreadReferenceProxyImpl thread = getContextThread();
+ RequestHint hint = new RequestHint(thread, suspendContext, StepRequest.STEP_OUT);
+ hint.setIgnoreFilters(mySession.shouldIgnoreSteppingFilters());
+ applyThreadFilter(thread);
+ final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher;
+ if (rvWatcher != null) {
+ rvWatcher.enable(thread.getThreadReference());
+ }
+ doStep(suspendContext, thread, StepRequest.STEP_OUT, hint);
+ super.contextAction();
+ }
+ }
+
+ private class StepIntoCommand extends ResumeCommand {
+ private final boolean myForcedIgnoreFilters;
+ private final RequestHint.SmartStepFilter mySmartStepFilter;
+
+ public StepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, @Nullable final RequestHint.SmartStepFilter smartStepFilter) {
+ super(suspendContext);
+ myForcedIgnoreFilters = ignoreFilters || smartStepFilter != null;
+ mySmartStepFilter = smartStepFilter;
+ }
+
+ public void contextAction() {
+ showStatusText(DebuggerBundle.message("status.step.into"));
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ final ThreadReferenceProxyImpl stepThread = getContextThread();
+ final RequestHint hint = mySmartStepFilter != null?
+ new RequestHint(stepThread, suspendContext, mySmartStepFilter) :
+ new RequestHint(stepThread, suspendContext, StepRequest.STEP_INTO);
+ if (myForcedIgnoreFilters) {
+ try {
+ mySession.setIgnoreStepFiltersFlag(stepThread.frameCount());
+ }
+ catch (EvaluateException e) {
+ LOG.info(e);
+ }
+ }
+ hint.setIgnoreFilters(myForcedIgnoreFilters || mySession.shouldIgnoreSteppingFilters());
+ applyThreadFilter(stepThread);
+ doStep(suspendContext, stepThread, StepRequest.STEP_INTO, hint);
+ super.contextAction();
+ }
+ }
+
+ private class StepOverCommand extends ResumeCommand {
+ private final boolean myIsIgnoreBreakpoints;
+
+ public StepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
+ super(suspendContext);
+ myIsIgnoreBreakpoints = ignoreBreakpoints;
+ }
+
+ public void contextAction() {
+ showStatusText(DebuggerBundle.message("status.step.over"));
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ final ThreadReferenceProxyImpl stepThread = getContextThread();
+ // need this hint whil stepping over for JSR45 support:
+ // several lines of generated java code may correspond to a single line in the source file,
+ // from which the java code was generated
+ RequestHint hint = new RequestHint(stepThread, suspendContext, StepRequest.STEP_OVER);
+ hint.setRestoreBreakpoints(myIsIgnoreBreakpoints);
+ hint.setIgnoreFilters(myIsIgnoreBreakpoints || mySession.shouldIgnoreSteppingFilters());
+
+ applyThreadFilter(stepThread);
+
+ final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher;
+ if (rvWatcher != null) {
+ rvWatcher.enable(stepThread.getThreadReference());
+ }
+
+ doStep(suspendContext, stepThread, StepRequest.STEP_OVER, hint);
+
+ if (myIsIgnoreBreakpoints) {
+ DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this);
+ }
+ super.contextAction();
+ }
+ }
+
+ private class RunToCursorCommand extends ResumeCommand {
+ private final RunToCursorBreakpoint myRunToCursorBreakpoint;
+ private final boolean myIgnoreBreakpoints;
+
+ private RunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex, final boolean ignoreBreakpoints) {
+ super(suspendContext);
+ myIgnoreBreakpoints = ignoreBreakpoints;
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+ myRunToCursorBreakpoint = breakpointManager.addRunToCursorBreakpoint(document, lineIndex, ignoreBreakpoints);
+ }
+
+ public void contextAction() {
+ showStatusText(DebuggerBundle.message("status.run.to.cursor"));
+ cancelRunToCursorBreakpoint();
+ if (myRunToCursorBreakpoint == null) {
+ return;
+ }
+ if (myIgnoreBreakpoints) {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+ breakpointManager.disableBreakpoints(DebugProcessImpl.this);
+ }
+ applyThreadFilter(getContextThread());
+ final SuspendContextImpl context = getSuspendContext();
+ myRunToCursorBreakpoint.SUSPEND_POLICY = context.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL;
+ myRunToCursorBreakpoint.LOG_ENABLED = false;
+ myRunToCursorBreakpoint.createRequest(context.getDebugProcess());
+ DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint;
+ super.contextAction();
+ }
+ }
+
+ public abstract class ResumeCommand extends SuspendContextCommandImpl {
+
+ private final ThreadReferenceProxyImpl myContextThread;
+
+ public ResumeCommand(SuspendContextImpl suspendContext) {
+ super(suspendContext);
+ final ThreadReferenceProxyImpl contextThread = mySession.getContextManager().getContext().getThreadProxy();
+ myContextThread = contextThread != null ? contextThread : getSuspendContext().getThread();
+ }
+
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+
+ public void contextAction() {
+ showStatusText(DebuggerBundle.message("status.process.resumed"));
+ getSuspendManager().resume(getSuspendContext());
+ myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext());
+ }
+
+ public ThreadReferenceProxyImpl getContextThread() {
+ return myContextThread;
+ }
+
+ protected void applyThreadFilter(ThreadReferenceProxy thread) {
+ if (getSuspendContext().getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
+ // there could be explicit resume as a result of call to voteSuspend()
+ // e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_
+ // resuming and all breakpoints in other threads will be ignored.
+ // As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
+ breakpointManager.applyThreadFilter(DebugProcessImpl.this, thread.getThreadReference());
+ }
+ }
+ }
+
+ private class PauseCommand extends DebuggerCommandImpl {
+ public PauseCommand() {
+ }
+
+ public void action() {
+ if (!isAttached() || getVirtualMachineProxy().isPausePressed()) {
+ return;
+ }
+ logThreads();
+ getVirtualMachineProxy().suspend();
+ logThreads();
+ SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_ALL, 0);
+ myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
+ }
+ }
+
+ private class ResumeThreadCommand extends SuspendContextCommandImpl {
+ private final ThreadReferenceProxyImpl myThread;
+
+ public ResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
+ super(suspendContext);
+ myThread = thread;
+ }
+
+ public void contextAction() {
+ if (getSuspendManager().isFrozen(myThread)) {
+ getSuspendManager().unfreezeThread(myThread);
+ return;
+ }
+
+ final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread);
+ for (SuspendContextImpl suspendContext : suspendingContexts) {
+ if (suspendContext.getThread() == myThread) {
+ DebugProcessImpl.this.getManagerThread().invoke(createResumeCommand(suspendContext));
+ }
+ else {
+ getSuspendManager().resumeThread(suspendContext, myThread);
+ }
+ }
+ }
+ }
+
+ private class FreezeThreadCommand extends DebuggerCommandImpl {
+ private final ThreadReferenceProxyImpl myThread;
+
+ public FreezeThreadCommand(ThreadReferenceProxyImpl thread) {
+ myThread = thread;
+ }
+
+ protected void action() throws Exception {
+ SuspendManager suspendManager = getSuspendManager();
+ if (!suspendManager.isFrozen(myThread)) {
+ suspendManager.freezeThread(myThread);
+ }
+ }
+ }
+
+ private class PopFrameCommand extends SuspendContextCommandImpl {
+ private final StackFrameProxyImpl myStackFrame;
+
+ public PopFrameCommand(SuspendContextImpl context, StackFrameProxyImpl frameProxy) {
+ super(context);
+ myStackFrame = frameProxy;
+ }
+
+ public void contextAction() {
+ final ThreadReferenceProxyImpl thread = myStackFrame.threadProxy();
+ try {
+ if (!getSuspendManager().isSuspended(thread)) {
+ notifyCancelled();
+ return;
+ }
+ }
+ catch (ObjectCollectedException e) {
+ notifyCancelled();
+ return;
+ }
+
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ if (!suspendContext.suspends(thread)) {
+ suspendContext.postponeCommand(this);
+ return;
+ }
+
+ if (myStackFrame.isBottom()) {
+ DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
+ public void run() {
+ Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.bottom.stackframe"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
+ }
+ });
+ return;
+ }
+
+ try {
+ thread.popFrames(myStackFrame);
+ }
+ catch (final EvaluateException e) {
+ DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
+ public void run() {
+ Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.stackframe", e.getLocalizedMessage()), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
+ }
+ });
+ LOG.info(e);
+ }
+ finally {
+ getSuspendManager().popFrame(suspendContext);
+ }
+ }
+ }
+
+ @NotNull
+ public GlobalSearchScope getSearchScope() {
+ LOG.assertTrue(mySession != null, "Accessing debug session before its initialization");
+ return mySession.getSearchScope();
+ }
+
+ @Nullable
+ public ExecutionResult attachVirtualMachine(final Executor executor,
+ final ProgramRunner runner,
+ final DebuggerSession session,
+ final RunProfileState state,
+ final RemoteConnection remoteConnection,
+ boolean pollConnection) throws ExecutionException {
+ return attachVirtualMachine(new DefaultDebugEnvironment(myProject,
+ executor,
+ runner,
+ state.getRunnerSettings().getRunProfile(),
+ state,
+ remoteConnection,
+ pollConnection),
+ session);
+ }
+
+ @Nullable
+ public ExecutionResult attachVirtualMachine(final DebugEnvironment environment,
+ final DebuggerSession session) throws ExecutionException {
+ mySession = session;
+ myWaitFor.down();
+
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ LOG.assertTrue(isInInitialState());
+
+ myConnection = environment.getRemoteConnection();
+
+ createVirtualMachine(environment.getSessionName(), environment.isPollConnection());
+
+ try {
+ synchronized (myProcessListeners) {
+ myExecutionResult = environment.createExecutionResult();
+ if (myExecutionResult == null) {
+ fail();
+ return null;
+ }
+ for (ProcessListener processListener : myProcessListeners) {
+ myExecutionResult.getProcessHandler().addProcessListener(processListener);
+ }
+ myProcessListeners.clear();
+ }
+ }
+ catch (ExecutionException e) {
+ fail();
+ throw e;
+ }
+
+ // writing to volatile field ensures the other threads will see the right values in non-volatile fields
+ myDebugProcessStarted = true;
+
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return myExecutionResult;
+ }
+
+ /*
+ final Alarm debugPortTimeout = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
+
+ myExecutionResult.getProcessHandler().addProcessListener(new ProcessAdapter() {
+ public void processTerminated(ProcessEvent event) {
+ debugPortTimeout.cancelAllRequests();
+ }
+
+ public void startNotified(ProcessEvent event) {
+ debugPortTimeout.addRequest(new Runnable() {
+ public void run() {
+ if(isInInitialState()) {
+ ApplicationManager.getApplication().schedule(new Runnable() {
+ public void run() {
+ String message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
+ Messages.showErrorDialog(myProject, message, DebuggerBundle.message("title.generic.debug.dialog"));
+ }
+ });
+ }
+ }
+ }, LOCAL_START_TIMEOUT);
+ }
+ });
+ */
+
+ return myExecutionResult;
+ }
+
+ private void fail() {
+ synchronized (this) {
+ if (myIsFailed) {
+ // need this in order to prevent calling stop() twice
+ return;
+ }
+ myIsFailed = true;
+ }
+ stop(false);
+ }
+
+ private void createVirtualMachine(final String sessionName, final boolean pollConnection) {
+ final Semaphore semaphore = new Semaphore();
+ semaphore.down();
+
+ final Ref<Boolean> connectorIsReady = Ref.create(false);
+ myDebugProcessDispatcher.addListener(new DebugProcessAdapter() {
+ public void connectorIsReady() {
+ connectorIsReady.set(true);
+ semaphore.up();
+ myDebugProcessDispatcher.removeListener(this);
+ }
+ });
+
+
+ this.getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() {
+ VirtualMachine vm = null;
+
+ try {
+ final long time = System.currentTimeMillis();
+ while (System.currentTimeMillis() - time < LOCAL_START_TIMEOUT) {
+ try {
+ vm = createVirtualMachineInt();
+ break;
+ }
+ catch (final ExecutionException e) {
+ if (pollConnection && !myConnection.isServerMode() && e.getCause() instanceof IOException) {
+ synchronized (this) {
+ try {
+ wait(500);
+ }
+ catch (InterruptedException ie) {
+ break;
+ }
+ }
+ }
+ else {
+ fail();
+ if (myExecutionResult != null || !connectorIsReady.get()) {
+ // propagate exception only in case we succeded to obtain execution result,
+ // otherwise if the error is induced by the fact that there is nothing to debug, and there is no need to show
+ // this problem to the user
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ExecutionUtil.handleExecutionError(myProject, ToolWindowId.DEBUG, sessionName, e);
+ }
+ });
+ }
+ break;
+ }
+ }
+ }
+ }
+ finally {
+ semaphore.up();
+ }
+
+ if (vm != null) {
+ final VirtualMachine vm1 = vm;
+ afterProcessStarted(new Runnable() {
+ public void run() {
+ getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ commitVM(vm1);
+ }
+ });
+ }
+ });
+ }
+ }
+
+ protected void commandCancelled() {
+ try {
+ super.commandCancelled();
+ }
+ finally {
+ semaphore.up();
+ }
+ }
+ });
+
+ semaphore.waitFor();
+ }
+
+ private void afterProcessStarted(final Runnable run) {
+ class MyProcessAdapter extends ProcessAdapter {
+ private boolean alreadyRun = false;
+
+ public synchronized void run() {
+ if(!alreadyRun) {
+ alreadyRun = true;
+ run.run();
+ }
+ removeProcessListener(this);
+ }
+
+ public void startNotified(ProcessEvent event) {
+ run();
+ }
+ }
+ MyProcessAdapter processListener = new MyProcessAdapter();
+ addProcessListener(processListener);
+ if(myExecutionResult != null) {
+ if(myExecutionResult.getProcessHandler().isStartNotified()) {
+ processListener.run();
+ }
+ }
+ }
+
+ public boolean isPausePressed() {
+ return myVirtualMachineProxy != null && myVirtualMachineProxy.isPausePressed();
+ }
+
+ public DebuggerCommandImpl createPauseCommand() {
+ return new PauseCommand();
+ }
+
+ public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext) {
+ return createResumeCommand(suspendContext, PrioritizedTask.Priority.HIGH);
+ }
+
+ public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext, final PrioritizedTask.Priority priority) {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
+ return new ResumeCommand(suspendContext) {
+ public void contextAction() {
+ breakpointManager.applyThreadFilter(DebugProcessImpl.this, null); // clear the filter on resume
+ super.contextAction();
+ }
+
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
+ return new StepOverCommand(suspendContext, ignoreBreakpoints);
+ }
+
+ public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext) {
+ return new StepOutCommand(suspendContext);
+ }
+
+ public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final RequestHint.SmartStepFilter smartStepFilter) {
+ return new StepIntoCommand(suspendContext, ignoreFilters, smartStepFilter);
+ }
+
+ public ResumeCommand createRunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex,
+ final boolean ignoreBreakpoints)
+ throws EvaluateException {
+ RunToCursorCommand runToCursorCommand = new RunToCursorCommand(suspendContext, document, lineIndex, ignoreBreakpoints);
+ if(runToCursorCommand.myRunToCursorBreakpoint == null) {
+ final PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
+ throw new EvaluateException(DebuggerBundle.message("error.running.to.cursor.no.executable.code", psiFile != null? psiFile.getName() : "<No File>", lineIndex), null);
+ }
+ return runToCursorCommand;
+ }
+
+ public DebuggerCommandImpl createFreezeThreadCommand(ThreadReferenceProxyImpl thread) {
+ return new FreezeThreadCommand(thread);
+ }
+
+ public SuspendContextCommandImpl createResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
+ return new ResumeThreadCommand(suspendContext, thread);
+ }
+
+ public SuspendContextCommandImpl createPopFrameCommand(DebuggerContextImpl context, StackFrameProxyImpl stackFrame) {
+ final SuspendContextImpl contextByThread =
+ SuspendManagerUtil.findContextByThread(context.getDebugProcess().getSuspendManager(), stackFrame.threadProxy());
+ return new PopFrameCommand(contextByThread, stackFrame);
+ }
+
+ public void setBreakpointsMuted(final boolean muted) {
+ if (isAttached()) {
+ getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ // set the flag before enabling/disabling cause it affects if breakpoints will create requests
+ if (myBreakpointsMuted.getAndSet(muted) != muted) {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+ if (muted) {
+ breakpointManager.disableBreakpoints(DebugProcessImpl.this);
+ }
+ else {
+ breakpointManager.enableBreakpoints(DebugProcessImpl.this);
+ }
+ }
+ }
+ });
+ }
+ else {
+ myBreakpointsMuted.set(muted);
+ }
+ }
+
+
+ public boolean areBreakpointsMuted() {
+ return myBreakpointsMuted.get();
+ }
+}
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java
new file mode 100644
index 0000000..df7a6d3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.managerThread.DebuggerCommand;
+import com.intellij.debugger.engine.managerThread.DebuggerManagerThread;
+import com.intellij.debugger.engine.managerThread.SuspendContextCommand;
+import com.intellij.debugger.impl.InvokeAndWaitThread;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter;
+import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.Alarm;
+import com.sun.jdi.VMDisconnectedException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.TestOnly;
+
+/**
+ * @author lex
+ */
+public class DebuggerManagerThreadImpl extends InvokeAndWaitThread<DebuggerCommandImpl> implements DebuggerManagerThread, Disposable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebuggerManagerThreadImpl");
+ public static final int COMMAND_TIMEOUT = 3000;
+ private static final int RESTART_TIMEOUT = 500;
+ private volatile boolean myDisposed;
+
+ DebuggerManagerThreadImpl(@NotNull Disposable parent) {
+ Disposer.register(parent, this);
+ }
+
+ public void dispose() {
+ myDisposed = true;
+ }
+
+ @TestOnly
+ public static DebuggerManagerThreadImpl createTestInstance(@NotNull Disposable parent) {
+ return new DebuggerManagerThreadImpl(parent);
+ }
+
+ public static boolean isManagerThread() {
+ return currentThread() instanceof DebuggerManagerThreadImpl;
+ }
+
+ public static void assertIsManagerThread() {
+ LOG.assertTrue(isManagerThread(), "Should be invoked in manager thread, use DebuggerManagerThreadImpl.getInstance(..).invoke...");
+ }
+
+ public void invokeAndWait(DebuggerCommandImpl managerCommand) {
+ LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
+ LOG.assertTrue(!(currentThread() instanceof DebuggerManagerThreadImpl),
+ "Should be invoked outside manager thread, use DebuggerManagerThreadImpl.getInstance(..).invoke...");
+ super.invokeAndWait(managerCommand);
+ }
+
+ public void invoke(DebuggerCommandImpl managerCommand) {
+ if (currentThread() instanceof DebuggerManagerThreadImpl) {
+ processEvent(managerCommand);
+ }
+ else {
+ schedule(managerCommand);
+ }
+ }
+
+ public boolean pushBack(DebuggerCommandImpl managerCommand) {
+ final boolean pushed = super.pushBack(managerCommand);
+ if (!pushed) {
+ managerCommand.notifyCancelled();
+ }
+ return pushed;
+ }
+
+ public boolean schedule(DebuggerCommandImpl managerCommand) {
+ final boolean scheduled = super.schedule(managerCommand);
+ if (!scheduled) {
+ managerCommand.notifyCancelled();
+ }
+ return scheduled;
+ }
+
+ /**
+ * waits COMMAND_TIMEOUT milliseconds
+ * if worker thread is still processing the same command
+ * calls terminateCommand
+ */
+ public void terminateAndInvoke(DebuggerCommandImpl command, int terminateTimeout) {
+ final DebuggerCommandImpl currentCommand = myEvents.getCurrentEvent();
+
+ invoke(command);
+
+ if (currentCommand != null) {
+ final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
+ alarm.addRequest(new Runnable() {
+ public void run() {
+ try {
+ if (currentCommand == myEvents.getCurrentEvent()) {
+ // if current command is still in progress, cancel it
+ getCurrentRequest().interrupt();
+ try {
+ getCurrentRequest().join();
+ }
+ catch (InterruptedException ignored) {
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ finally {
+ if (!myDisposed) {
+ startNewWorkerThread();
+ }
+ }
+ }
+ }
+ finally {
+ Disposer.dispose(alarm);
+ }
+ }
+ }, terminateTimeout);
+ }
+ }
+
+
+ public void processEvent(@NotNull DebuggerCommandImpl managerCommand) {
+ assertIsManagerThread();
+ try {
+ if(myEvents.isClosed()) {
+ managerCommand.notifyCancelled();
+ }
+ else {
+ managerCommand.run();
+ }
+ }
+ catch (VMDisconnectedException e) {
+ LOG.debug(e);
+ }
+ catch (RuntimeException e) {
+ throw e;
+ }
+ catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ }
+ }
+
+ public void startProgress(final DebuggerCommandImpl command, final ProgressWindowWithNotification progressWindow) {
+ progressWindow.addListener(new ProgressIndicatorListenerAdapter() {
+ public void cancelled() {
+ command.release();
+ }
+ });
+
+ ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
+ public void run() {
+ ProgressManager.getInstance().runProcess(new Runnable() {
+ public void run() {
+ invokeAndWait(command);
+ }
+ }, progressWindow);
+ }
+ });
+ }
+
+
+ public void startLongProcessAndFork(Runnable process) {
+ startNewWorkerThread();
+
+ try {
+ process.run();
+ }
+ finally {
+ final WorkerThreadRequest request = getCurrentThreadRequest();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Switching back to " + request);
+ }
+
+ super.invokeAndWait(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ switchToRequest(request);
+ }
+
+ protected void commandCancelled() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Event queue was closed, killing request");
+ }
+ request.interrupt();
+ }
+ });
+ }
+ }
+
+ public void invokeCommand(final DebuggerCommand command) {
+ if(command instanceof SuspendContextCommand) {
+ SuspendContextCommand suspendContextCommand = (SuspendContextCommand)command;
+ schedule(new SuspendContextCommandImpl((SuspendContextImpl)suspendContextCommand.getSuspendContext()) {
+ public void contextAction() throws Exception {
+ command.action();
+ }
+
+ protected void commandCancelled() {
+ command.commandCancelled();
+ }
+ });
+ }
+ else {
+ schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ command.action();
+ }
+
+ protected void commandCancelled() {
+ command.commandCancelled();
+ }
+ });
+ }
+
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JVMName.java b/java/debugger/impl/src/com/intellij/debugger/engine/JVMName.java
new file mode 100644
index 0000000..9d47e31
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/JVMName.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+
+/**
+ * @author lex
+ */
+public interface JVMName {
+ String getName(DebugProcessImpl process) throws EvaluateException;
+
+ String getDisplayName(DebugProcessImpl debugProcess);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java
new file mode 100644
index 0000000..edee6bb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManager;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Ref;
+import com.intellij.psi.*;
+import com.intellij.psi.jsp.JspFile;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.psi.util.TypeConversionUtil;
+import com.sun.jdi.ReferenceType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 2, 2003
+ * Time: 11:25:59 AM
+ */
+public class JVMNameUtil {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.JVMNameUtil");
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public static String getPrimitiveSignature(String typeName) {
+ if(PsiType.BOOLEAN.getCanonicalText().equals(typeName)) {
+ return "Z";
+ }
+ else if (PsiType.BYTE.getCanonicalText().equals(typeName)) {
+ return "B";
+ }
+ else if (PsiType.CHAR.getCanonicalText().equals(typeName)) {
+ return "C";
+ }
+ else if (PsiType.SHORT.getCanonicalText().equals(typeName)) {
+ return "S";
+ }
+ else if (PsiType.INT.getCanonicalText().equals(typeName)) {
+ return "I";
+ }
+ else if (PsiType.LONG.getCanonicalText().equals(typeName)) {
+ return "J";
+ }
+ else if (PsiType.FLOAT.getCanonicalText().equals(typeName)) {
+ return "F";
+ }
+ else if (PsiType.DOUBLE.getCanonicalText().equals(typeName)) {
+ return "D";
+ }
+ else if (PsiType.VOID.getCanonicalText().equals(typeName)) {
+ return "V";
+ }
+ return null;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void appendJVMSignature(JVMNameBuffer buffer , PsiType type){
+ if (type == null) {
+ return;
+ }
+ final PsiType psiType = TypeConversionUtil.erasure(type);
+ if (psiType instanceof PsiArrayType) {
+ buffer.append(new JVMRawText("["));
+ appendJVMSignature(buffer, ((PsiArrayType) psiType).getComponentType());
+ }
+ else if (psiType instanceof PsiClassType) {
+ final JVMName jvmName = getJVMQualifiedName(psiType);
+ appendJvmClassQualifiedName(buffer, jvmName);
+ }
+ else if (psiType instanceof PsiPrimitiveType) {
+ buffer.append(getPrimitiveSignature(psiType.getCanonicalText()));
+ }
+ else {
+ LOG.error("unknown type " + type.getCanonicalText());
+ }
+ }
+
+ private static void appendJvmClassQualifiedName(JVMNameBuffer buffer, final JVMName jvmName) {
+ buffer.append("L");
+ if(jvmName instanceof JVMRawText) {
+ buffer.append(((JVMRawText)jvmName).getName().replace('.','/'));
+ }
+ else {
+ buffer.append(new JVMName() {
+ public String getName(DebugProcessImpl process) throws EvaluateException {
+ return jvmName.getName(process).replace('.','/');
+ }
+
+ public String getDisplayName(DebugProcessImpl debugProcess) {
+ return jvmName.getDisplayName(debugProcess);
+ }
+ });
+ }
+ buffer.append(";");
+ }
+
+ private static class JVMNameBuffer {
+ List<JVMName> myList = new ArrayList<JVMName>();
+
+ public void append(@NotNull JVMName evaluator){
+ myList.add(evaluator);
+ }
+
+ public void append(char name){
+ append(Character.toString(name));
+ }
+
+ public void append(String text){
+ myList.add(getJVMRawText(text));
+ }
+
+ public JVMName toName() {
+ final List<JVMName> optimised = new ArrayList<JVMName>();
+ for (JVMName evaluator : myList) {
+ if (evaluator instanceof JVMRawText && !optimised.isEmpty() && optimised.get(optimised.size() - 1) instanceof JVMRawText) {
+ JVMRawText nameEvaluator = (JVMRawText)optimised.get(optimised.size() - 1);
+ nameEvaluator.setName(nameEvaluator.getName() + ((JVMRawText)evaluator).getName());
+ }
+ else {
+ optimised.add(evaluator);
+ }
+ }
+
+ if(optimised.size() == 1) return optimised.get(0);
+ if(optimised.isEmpty()) return new JVMRawText("");
+
+ return new JVMName() {
+ String myName = null;
+ public String getName(DebugProcessImpl process) throws EvaluateException {
+ if(myName == null){
+ String name = "";
+ for (JVMName nameEvaluator : optimised) {
+ name += nameEvaluator.getName(process);
+ }
+ myName = name;
+ }
+ return myName;
+ }
+
+ public String getDisplayName(DebugProcessImpl debugProcess) {
+ if(myName == null) {
+ String displayName = "";
+ for (JVMName nameEvaluator : optimised) {
+ displayName += nameEvaluator.getDisplayName(debugProcess);
+ }
+ return displayName;
+ }
+ return myName;
+ }
+ };
+ }
+ }
+
+ private static class JVMRawText implements JVMName {
+ private String myText;
+
+ public JVMRawText(String text) {
+ myText = text;
+ }
+
+ public String getName(DebugProcessImpl process) throws EvaluateException {
+ return myText;
+ }
+
+ public String getDisplayName(DebugProcessImpl debugProcess) {
+ return myText;
+ }
+
+ public String getName() {
+ return myText;
+ }
+
+ public void setName(String name) {
+ myText = name;
+ }
+ }
+
+ private static class JVMClassAt implements JVMName {
+ private final SourcePosition mySourcePosition;
+
+ public JVMClassAt(SourcePosition sourcePosition) {
+ mySourcePosition = sourcePosition;
+ }
+
+ public String getName(DebugProcessImpl process) throws EvaluateException {
+ List<ReferenceType> allClasses = process.getPositionManager().getAllClasses(mySourcePosition);
+ if(!allClasses.isEmpty()) {
+ return allClasses.get(0).name();
+ }
+
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.class.not.loaded", getDisplayName(process)));
+ }
+
+ public String getDisplayName(final DebugProcessImpl debugProcess) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ public String compute() {
+ return getSourcePositionClassDisplayName(debugProcess, mySourcePosition);
+ }
+ });
+ }
+ }
+
+ public static JVMName getJVMRawText(String qualifiedName) {
+ return new JVMRawText(qualifiedName);
+ }
+
+ public static JVMName getJVMQualifiedName(PsiType psiType) {
+ if(psiType instanceof PsiArrayType) {
+ final PsiArrayType arrayType = (PsiArrayType)psiType;
+ JVMName jvmName = getJVMQualifiedName(arrayType.getComponentType());
+ JVMNameBuffer buffer = new JVMNameBuffer();
+ buffer.append(jvmName);
+ buffer.append("[]");
+ return buffer.toName();
+ }
+
+ PsiClass psiClass = PsiUtil.resolveClassInType(psiType);
+ if (psiClass == null) {
+ return getJVMRawText(psiType.getCanonicalText());
+ }
+ else {
+ return getJVMQualifiedName(psiClass);
+ }
+ }
+
+ public static JVMName getJVMQualifiedName(PsiClass psiClass) {
+ if (!PsiUtil.isLocalOrAnonymousClass(psiClass)) {
+ final String name = getNonAnonymousClassName(psiClass);
+ if (name != null) {
+ return getJVMRawText(name);
+ }
+ }
+ return new JVMClassAt(SourcePosition.createFromElement(psiClass));
+ }
+
+ @Nullable
+ public static JVMName getContextClassJVMQualifiedName(@Nullable SourcePosition pos) {
+ if (pos == null) {
+ return null;
+ }
+ final PsiClass psiClass = getClassAt(pos);
+ if (psiClass == null) {
+ return null;
+ }
+ if (!PsiUtil.isLocalOrAnonymousClass(psiClass)) {
+ final String name = getNonAnonymousClassName(psiClass);
+ if (name != null) {
+ return getJVMRawText(name);
+ }
+ }
+ return new JVMClassAt(pos);
+ }
+
+ @Nullable
+ public static String getNonAnonymousClassName(PsiClass aClass) {
+ PsiClass parentClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
+ if(parentClass != null) {
+ final String parentName = getNonAnonymousClassName(parentClass);
+ if (parentName == null) {
+ return null;
+ }
+ return parentName + "$" + aClass.getName();
+ }
+ return DebuggerManager.getInstance(aClass.getProject()).getVMClassQualifiedName(aClass);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public static JVMName getJVMSignature(PsiMethod method) {
+ JVMNameBuffer signature = new JVMNameBuffer();
+ signature.append("(");
+
+ if (method.isConstructor()) {
+ final PsiClass declaringClass = method.getContainingClass();
+ if (declaringClass != null) {
+ final PsiClass outerClass = declaringClass.getContainingClass();
+ if (outerClass != null) {
+ // declaring class is an inner class
+ if (!declaringClass.hasModifierProperty(PsiModifier.STATIC)) {
+ appendJvmClassQualifiedName(signature, getJVMQualifiedName(outerClass));
+ }
+ }
+ }
+ }
+ for (PsiParameter psiParameter : method.getParameterList().getParameters()) {
+ appendJVMSignature(signature, psiParameter.getType());
+ }
+ signature.append(")");
+ if (!method.isConstructor()) {
+ appendJVMSignature(signature, method.getReturnType());
+ }
+ else {
+ signature.append(new JVMRawText("V"));
+ }
+ return signature.toName();
+ }
+
+ @Nullable
+ public static PsiClass getClassAt(SourcePosition position) {
+ final PsiElement element = position.getElementAt();
+ return (element != null) ? PsiTreeUtil.getParentOfType(element, PsiClass.class, false) : null;
+ }
+
+ @Nullable
+ public static String getSourcePositionClassDisplayName(DebugProcessImpl debugProcess, SourcePosition position) {
+ if (position == null) {
+ return null;
+ }
+ final PsiFile positionFile = position.getFile();
+ if (positionFile instanceof JspFile) {
+ return positionFile.getName();
+ }
+
+ final PsiClass psiClass = getClassAt(position);
+
+ if(psiClass != null) {
+ final String qName = psiClass.getQualifiedName();
+ if(qName != null) {
+ return qName;
+ }
+ }
+
+ if(debugProcess != null && debugProcess.isAttached()) {
+ List<ReferenceType> allClasses = debugProcess.getPositionManager().getAllClasses(position);
+ if(!allClasses.isEmpty()) {
+ return allClasses.get(0).name();
+ }
+ }
+ if (psiClass == null) {
+ if (positionFile instanceof PsiClassOwner) {
+ return positionFile.getName();
+ }
+
+ return DebuggerBundle.message("string.file.line.position", positionFile.getName(), position.getLine());
+ }
+ return calcClassDisplayName(psiClass);
+ }
+
+ static String calcClassDisplayName(final PsiClass aClass) {
+ final String qName = aClass.getQualifiedName();
+ if (qName != null) {
+ return qName;
+ }
+ final PsiClass parent = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
+ if (parent == null) {
+ return null;
+ }
+
+ final String name = aClass.getName();
+ if (name != null) {
+ return calcClassDisplayName(parent) + "$" + name;
+ }
+
+ final Ref<Integer> classIndex = new Ref<Integer>(0);
+ try {
+ parent.accept(new JavaRecursiveElementVisitor() {
+ public void visitAnonymousClass(PsiAnonymousClass cls) {
+ classIndex.set(classIndex.get() + 1);
+ if (aClass.equals(cls)) {
+ throw new ProcessCanceledException();
+ }
+ }
+ });
+ }
+ catch (ProcessCanceledException ignored) {
+ }
+ return calcClassDisplayName(parent) + "$" + classIndex.get();
+ }
+
+ @Nullable
+ public static String getSourcePositionPackageDisplayName(DebugProcessImpl debugProcess, SourcePosition position) {
+ if (position == null) {
+ return null;
+ }
+ final PsiFile positionFile = position.getFile();
+ if (positionFile instanceof JspFile) {
+ final PsiDirectory dir = positionFile.getContainingDirectory();
+ return dir != null? dir.getVirtualFile().getPresentableUrl() : null;
+ }
+
+ final PsiClass psiClass = getClassAt(position);
+
+ if(psiClass != null) {
+ PsiClass toplevel = PsiUtil.getTopLevelClass(psiClass);
+ if(toplevel != null) {
+ String qName = toplevel.getQualifiedName();
+ if (qName != null) {
+ int i = qName.lastIndexOf('.');
+ return i > 0 ? qName.substring(0, i) : "";
+ }
+ }
+ }
+
+ if(debugProcess != null && debugProcess.isAttached()) {
+ List<ReferenceType> allClasses = debugProcess.getPositionManager().getAllClasses(position);
+ if(!allClasses.isEmpty()) {
+ final String className = allClasses.get(0).name();
+ int dotIndex = className.lastIndexOf('.');
+ if (dotIndex >= 0) {
+ return className.substring(0, dotIndex);
+ }
+ }
+ }
+ return "";
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java
new file mode 100644
index 0000000..4909e74
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.NoDataException;
+import com.intellij.debugger.PositionManager;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.NullableComputable;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.Trinity;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiUtil;
+import com.sun.jdi.AbsentInformationException;
+import com.sun.jdi.Location;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.request.ClassPrepareRequest;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author lex
+ */
+public class PositionManagerImpl implements PositionManager {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.PositionManagerImpl");
+
+ private final DebugProcessImpl myDebugProcess;
+
+ public PositionManagerImpl(DebugProcessImpl debugProcess) {
+ myDebugProcess = debugProcess;
+ }
+
+ public DebugProcess getDebugProcess() {
+ return myDebugProcess;
+ }
+
+ @NotNull
+ public List<Location> locationsOfLine(ReferenceType type, SourcePosition position) throws NoDataException {
+ try {
+ final int line = position.getLine() + 1;
+ return type.locationsOfLine(DebugProcess.JAVA_STRATUM, null, line);
+ }
+ catch (AbsentInformationException ignored) {
+ }
+ return Collections.emptyList();
+ }
+
+ public ClassPrepareRequest createPrepareRequest(final ClassPrepareRequestor requestor, final SourcePosition position) throws NoDataException {
+ final Ref<String> waitPrepareFor = new Ref<String>(null);
+ final Ref<ClassPrepareRequestor> waitRequestor = new Ref<ClassPrepareRequestor>(null);
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ PsiClass psiClass = JVMNameUtil.getClassAt(position);
+ if (psiClass == null) {
+ return;
+ }
+
+ if (PsiUtil.isLocalOrAnonymousClass(psiClass)) {
+ PsiClass parent = TopLevelParentClassProvider.getTopLevelParentClass(psiClass);
+
+ if (parent == null) {
+ return;
+ }
+
+ final String parentQName = JVMNameUtil.getNonAnonymousClassName(parent);
+ if (parentQName == null) {
+ return;
+ }
+ waitPrepareFor.set(parentQName + "$*");
+ waitRequestor.set(new ClassPrepareRequestor() {
+ public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) {
+ final CompoundPositionManager positionManager = ((DebugProcessImpl)debuggerProcess).getPositionManager();
+ final List<ReferenceType> positionClasses = positionManager.getAllClasses(position);
+ if (positionClasses.isEmpty()) {
+ // fallback
+ if (positionManager.locationsOfLine(referenceType, position).size() > 0) {
+ requestor.processClassPrepare(debuggerProcess, referenceType);
+ }
+ }
+ else {
+ if (positionClasses.contains(referenceType)) {
+ requestor.processClassPrepare(debuggerProcess, referenceType);
+ }
+ }
+ }
+ });
+ }
+ else {
+ waitPrepareFor.set(JVMNameUtil.getNonAnonymousClassName(psiClass));
+ waitRequestor.set(requestor);
+ }
+ }
+ });
+ if (waitPrepareFor.get() == null) {
+ return null; // no suitable class found for this name
+ }
+ return myDebugProcess.getRequestsManager().createClassPrepareRequest(waitRequestor.get(), waitPrepareFor.get());
+ }
+
+ public SourcePosition getSourcePosition(final Location location) throws NoDataException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if(location == null) {
+ return null;
+ }
+
+ PsiFile psiFile = getPsiFileByLocation(getDebugProcess().getProject(), location);
+ if(psiFile == null ) {
+ return null;
+ }
+
+ LOG.assertTrue(myDebugProcess != null);
+ if (location == null) {
+ return SourcePosition.createFromLine(psiFile, -1);
+ }
+
+ int lineNumber;
+ try {
+ lineNumber = location.lineNumber() - 1;
+ }
+ catch (InternalError e) {
+ lineNumber = -1;
+ }
+
+ if (psiFile instanceof PsiCompiledElement || lineNumber < 0) {
+ final String methodSignature = location.method().signature();
+ if (methodSignature == null) {
+ return SourcePosition.createFromLine(psiFile, -1);
+ }
+ final String methodName = location.method().name();
+ if(methodName == null) {
+ return SourcePosition.createFromLine(psiFile, -1);
+ }
+ if(location.declaringType() == null) {
+ return SourcePosition.createFromLine(psiFile, -1);
+ }
+
+ final MethodFinder finder = new MethodFinder(location.declaringType().name(), methodSignature);
+ psiFile.accept(finder);
+
+ final PsiMethod compiledMethod = finder.getCompiledMethod();
+ if (compiledMethod == null) {
+ return SourcePosition.createFromLine(psiFile, -1);
+ }
+ return SourcePosition.createFromElement(compiledMethod);
+ }
+
+ return SourcePosition.createFromLine(psiFile, lineNumber);
+ }
+
+ @Nullable
+ private PsiFile getPsiFileByLocation(final Project project, final Location location) {
+ if (location == null) {
+ return null;
+ }
+ final ReferenceType refType = location.declaringType();
+ if (refType == null) {
+ return null;
+ }
+
+ if (DumbService.getInstance(project).isDumb()) {
+ return null;
+ }
+
+ final String originalQName = refType.name();
+ int dollar = originalQName.indexOf('$');
+ final String qName = dollar >= 0 ? originalQName.substring(0, dollar) : originalQName;
+ final GlobalSearchScope searchScope = myDebugProcess.getSearchScope();
+ PsiClass psiClass = DebuggerUtils.findClass(qName, project, searchScope);
+ if (psiClass == null && dollar >= 0 /*originalName and qName really differ*/) {
+ psiClass = DebuggerUtils.findClass(originalQName, project, searchScope); // try to lookup original name
+ }
+
+ if (psiClass != null) {
+ final PsiElement element = psiClass.getNavigationElement();
+ return element.getContainingFile();
+ }
+
+ return null;
+ }
+
+ @NotNull
+ public List<ReferenceType> getAllClasses(final SourcePosition classPosition) throws NoDataException {
+ final Trinity<String, Boolean, PsiClass> trinity = calcClassName(classPosition);
+ if (trinity == null) {
+ return Collections.emptyList();
+ }
+ final String className = trinity.getFirst();
+ final boolean isNonAnonymousClass = trinity.getSecond();
+ final PsiClass classAtPosition = trinity.getThird();
+
+ if (isNonAnonymousClass) {
+ return myDebugProcess.getVirtualMachineProxy().classesByName(className);
+ }
+
+ // the name is a parent class for a local or anonymous class
+ final List<ReferenceType> outers = myDebugProcess.getVirtualMachineProxy().classesByName(className);
+ final List<ReferenceType> result = new ArrayList<ReferenceType>(outers.size());
+ for (ReferenceType outer : outers) {
+ final ReferenceType nested = findNested(outer, classAtPosition, classPosition);
+ if (nested != null) {
+ result.add(nested);
+ }
+ }
+ return result;
+ }
+
+ @Nullable
+ private static Trinity<String, Boolean, PsiClass> calcClassName(final SourcePosition classPosition) {
+ return ApplicationManager.getApplication().runReadAction(new NullableComputable<Trinity<String, Boolean, PsiClass>>() {
+ public Trinity<String, Boolean, PsiClass> compute() {
+ final PsiClass psiClass = JVMNameUtil.getClassAt(classPosition);
+
+ if(psiClass == null) {
+ return null;
+ }
+
+ if(PsiUtil.isLocalOrAnonymousClass(psiClass)) {
+ final PsiClass parentNonLocal = TopLevelParentClassProvider.getTopLevelParentClass(psiClass);
+ if(parentNonLocal == null) {
+ LOG.error("Local or anonymous class has no non-local parent");
+ return null;
+ }
+ final String parentClassName = JVMNameUtil.getNonAnonymousClassName(parentNonLocal);
+ if(parentClassName == null) {
+ LOG.error("The name of a parent of a local (anonymous) class is null");
+ return null;
+ }
+ return new Trinity<String, Boolean, PsiClass>(parentClassName, Boolean.FALSE, psiClass);
+ }
+
+ final String className = JVMNameUtil.getNonAnonymousClassName(psiClass);
+ return className != null? new Trinity<String, Boolean, PsiClass>(className, Boolean.TRUE, psiClass) : null;
+ }
+ });
+ }
+
+ @Nullable
+ private ReferenceType findNested(final ReferenceType fromClass, final PsiClass classToFind, SourcePosition classPosition) {
+ final VirtualMachineProxyImpl vmProxy = myDebugProcess.getVirtualMachineProxy();
+ if (fromClass.isPrepared()) {
+
+ final List<ReferenceType> nestedTypes = vmProxy.nestedTypes(fromClass);
+
+ try {
+ final int lineNumber = classPosition.getLine() + 1;
+
+ for (ReferenceType nested : nestedTypes) {
+ final ReferenceType found = findNested(nested, classToFind, classPosition);
+ if (found != null) {
+ // check if enclosing class also has executable code at the same line, and if yes, prefer enclosing class
+ return fromClass.locationsOfLine(lineNumber).isEmpty()? found : fromClass;
+ }
+ }
+
+ if (fromClass.locationsOfLine(lineNumber).size() > 0) {
+ return fromClass;
+ }
+
+ int rangeBegin = Integer.MAX_VALUE;
+ int rangeEnd = Integer.MIN_VALUE;
+ for (Location location : fromClass.allLineLocations()) {
+ final int locationLine = location.lineNumber() - 1;
+ rangeBegin = Math.min(rangeBegin, locationLine);
+ rangeEnd = Math.max(rangeEnd, locationLine);
+ }
+
+ if (classPosition.getLine() >= rangeBegin && classPosition.getLine() <= rangeEnd) {
+ // choose the second line to make sure that only this class' code exists on the line chosen
+ // Otherwise the line (depending on the offset in it) can contain code that belongs to different classes
+ // and JVMNameUtil.getClassAt(candidatePosition) will return the wrong class.
+ // Example of such line:
+ // list.add(new Runnable(){......
+ // First offsets belong to parent class, and offsets inside te substring "new Runnable(){" belong to anonymous runnable.
+ final int finalRangeBegin = rangeBegin;
+ final int finalRangeEnd = rangeEnd;
+ return ApplicationManager.getApplication().runReadAction(new NullableComputable<ReferenceType>() {
+ public ReferenceType compute() {
+ if (!classToFind.isValid()) {
+ return null;
+ }
+ final int line = Math.min(finalRangeBegin + 1, finalRangeEnd);
+ final SourcePosition candidatePosition = SourcePosition.createFromLine(classToFind.getContainingFile(), line);
+ return classToFind.equals(JVMNameUtil.getClassAt(candidatePosition)) ? fromClass : null;
+ }
+ });
+ }
+ }
+ catch (AbsentInformationException ignored) {
+ }
+ }
+ return null;
+ }
+
+ //don't use JavaRecursiveElementWalkingVisitor because getNextSibling() works slowly for compiled elements
+ private class MethodFinder extends JavaRecursiveElementVisitor {
+ private final String myClassName;
+ private PsiClass myCompiledClass;
+ private final String myMethodSignature;
+ private PsiMethod myCompiledMethod;
+
+ public MethodFinder(final String className, final String methodSignature) {
+ myClassName = className;
+ myMethodSignature = methodSignature;
+ }
+
+ @Override public void visitClass(PsiClass aClass) {
+ final List<ReferenceType> allClasses = myDebugProcess.getPositionManager().getAllClasses(SourcePosition.createFromElement(aClass));
+ for (ReferenceType referenceType : allClasses) {
+ if (referenceType.name().equals(myClassName)) {
+ myCompiledClass = aClass;
+ }
+ }
+
+ aClass.acceptChildren(this);
+ }
+
+ @Override public void visitMethod(PsiMethod method) {
+ try {
+ //noinspection HardCodedStringLiteral
+ String methodName = method.isConstructor() ? "<init>" : method.getName();
+ PsiClass containingClass = method.getContainingClass();
+
+ if(containingClass != null &&
+ containingClass.equals(myCompiledClass) &&
+ methodName.equals(methodName) &&
+ JVMNameUtil.getJVMSignature(method).getName(myDebugProcess).equals(myMethodSignature)) {
+
+ myCompiledMethod = method;
+ }
+ }
+ catch (EvaluateException e) {
+ LOG.debug(e);
+ }
+ }
+
+ public PsiClass getCompiledClass() {
+ return myCompiledClass;
+ }
+
+ public PsiMethod getCompiledMethod() {
+ return myCompiledMethod;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/RemoteDebugProcessHandler.java b/java/debugger/impl/src/com/intellij/debugger/engine/RemoteDebugProcessHandler.java
new file mode 100644
index 0000000..84e3032
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/RemoteDebugProcessHandler.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.DebuggerManager;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.openapi.project.Project;
+
+import java.io.OutputStream;
+
+public class RemoteDebugProcessHandler extends ProcessHandler{
+ private final Project myProject;
+
+ public RemoteDebugProcessHandler(Project project) {
+ myProject = project;
+ }
+
+ public void startNotify() {
+ final DebugProcess debugProcess = DebuggerManager.getInstance(myProject).getDebugProcess(this);
+ final DebugProcessAdapter listener = new DebugProcessAdapter() {
+ //executed in manager thread
+ public void processDetached(DebugProcess process, boolean closedByUser) {
+ debugProcess.removeDebugProcessListener(this);
+ notifyProcessDetached();
+ }
+ };
+ debugProcess.addDebugProcessListener(listener);
+ try {
+ super.startNotify();
+ }
+ finally {
+ // in case we added our listener too late, we may have lost processDetached notification,
+ // so check here if process is detached
+ if (debugProcess.isDetached()) {
+ debugProcess.removeDebugProcessListener(listener);
+ notifyProcessDetached();
+ }
+ }
+ }
+
+ protected void destroyProcessImpl() {
+ DebugProcess debugProcess = DebuggerManager.getInstance(myProject).getDebugProcess(this);
+ if(debugProcess != null) {
+ debugProcess.stop(true);
+ }
+ }
+
+ protected void detachProcessImpl() {
+ DebugProcess debugProcess = DebuggerManager.getInstance(myProject).getDebugProcess(this);
+ if(debugProcess != null) {
+ debugProcess.stop(false);
+ }
+ }
+
+ public boolean detachIsDefault() {
+ return true;
+ }
+
+ public OutputStream getProcessInput() {
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/RemoteStateState.java b/java/debugger/impl/src/com/intellij/debugger/engine/RemoteStateState.java
new file mode 100644
index 0000000..4205f24
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/RemoteStateState.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.execution.DefaultExecutionResult;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RemoteState;
+import com.intellij.execution.configurations.RunnerSettings;
+import com.intellij.execution.impl.ConsoleViewImpl;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author lex
+ */
+public class RemoteStateState implements RemoteState {
+ private final Project myProject;
+ private final RemoteConnection myConnection;
+ private final RunnerSettings myRunnerSettings;
+ private final ConfigurationPerRunnerSettings myConfigurationSettings;
+
+ public RemoteStateState(Project project,
+ RemoteConnection connection,
+ RunnerSettings runnerSettings,
+ ConfigurationPerRunnerSettings configurationSettings) {
+ myProject = project;
+ myConnection = connection;
+ myRunnerSettings = runnerSettings;
+ myConfigurationSettings = configurationSettings;
+ }
+
+ public RunnerSettings getRunnerSettings() {
+ return myRunnerSettings;
+ }
+
+ public ConfigurationPerRunnerSettings getConfigurationSettings() {
+ return myConfigurationSettings;
+ }
+
+ public ExecutionResult execute(final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
+ ConsoleViewImpl consoleView = new ConsoleViewImpl(myProject, false);
+ RemoteDebugProcessHandler process = new RemoteDebugProcessHandler(myProject);
+ consoleView.attachToProcess(process);
+ return new DefaultExecutionResult(consoleView, process);
+ }
+
+ public RemoteConnection getRemoteConnection() {
+ return myConnection;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java b/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java
new file mode 100644
index 0000000..1a94cbc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jul 23, 2002
+ * Time: 11:10:11 AM
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.sun.jdi.*;
+import com.sun.jdi.request.StepRequest;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class RequestHint {
+ public static final int STOP = 0;
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.RequestHint");
+ private final int myDepth;
+ private SourcePosition myPosition;
+ private int myFrameCount;
+ private VirtualMachineProxyImpl myVirtualMachineProxy;
+
+ private final @Nullable SmartStepFilter myTargetMethodSignature;
+ private boolean myIgnoreFilters = false;
+ private boolean myRestoreBreakpoints = false;
+ private final boolean mySkipThisMethod = false;
+
+ public static final class SmartStepFilter {
+ private final JVMName myDeclaringClassName;
+ private final @NonNls String myTargetMethodName;
+ private final JVMName myTargetMethodSignature;
+ private boolean myMethodExecuted;
+
+ public SmartStepFilter(PsiMethod psiMethod) {
+ this(JVMNameUtil.getJVMQualifiedName(psiMethod.getContainingClass()),
+ psiMethod.isConstructor()? "<init>" : psiMethod.getName(),
+ JVMNameUtil.getJVMSignature(psiMethod));
+ }
+
+ public SmartStepFilter(@NotNull JVMName declaringClassName, @NonNls String targetMethodName,
+ @NotNull JVMName targetMethodSignature) {
+ myDeclaringClassName = declaringClassName;
+ myTargetMethodName = targetMethodName;
+ myTargetMethodSignature = targetMethodSignature;
+ }
+
+ public String getTargetMethodName() {
+ return myTargetMethodName;
+ }
+
+ public boolean wasMethodExecuted() {
+ return myMethodExecuted;
+ }
+
+ public boolean shouldStopAtLocation(final SuspendContextImpl context) {
+ try {
+ final StackFrameProxyImpl frameProxy = context.getFrameProxy();
+ if (frameProxy == null) {
+ return true;
+ }
+ final Location location = frameProxy.location();
+ final Method method = location.method();
+ if (!myTargetMethodName.equals(method.name())) {
+ return false;
+ }
+ final DebugProcessImpl process = context.getDebugProcess();
+ if (!signatureMatches(method, myTargetMethodSignature.getName(process))) {
+ return false;
+ }
+ myMethodExecuted = true;
+ final ObjectReference thisObject = frameProxy.thisObject();
+ final ReferenceType locationClass = thisObject != null? thisObject.referenceType() : method.declaringType();
+ return DebuggerUtilsEx.isAssignableFrom(myDeclaringClassName.getName(process), locationClass);
+ }
+ catch (EvaluateException e) {
+ LOG.info(e);
+ }
+ return true;
+ }
+
+ private static boolean signatureMatches(Method method, final String expectedSignature) throws EvaluateException {
+ if (expectedSignature.equals(method.signature())) {
+ return true;
+ }
+ // check if there are any bridge methods that match
+ for (Method candidate : method.declaringType().methodsByName(method.name())) {
+ if (candidate != method && candidate.isBridge() && expectedSignature.equals(candidate.signature())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public RequestHint(final ThreadReferenceProxyImpl stepThread, final SuspendContextImpl suspendContext, @NotNull SmartStepFilter smartStepFilter) {
+ this(stepThread, suspendContext, StepRequest.STEP_INTO, smartStepFilter);
+ }
+
+ public RequestHint(final ThreadReferenceProxyImpl stepThread, final SuspendContextImpl suspendContext, int depth) {
+ this(stepThread, suspendContext, depth, null);
+ }
+
+ private RequestHint(final ThreadReferenceProxyImpl stepThread, final SuspendContextImpl suspendContext, int depth, SmartStepFilter smartStepFilter) {
+ final DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
+ myDepth = depth;
+ myTargetMethodSignature = smartStepFilter;
+ myVirtualMachineProxy = debugProcess.getVirtualMachineProxy();
+
+ try {
+ myFrameCount = stepThread.frameCount();
+
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ myPosition = ContextUtil.getSourcePosition(new StackFrameContext() {
+ public StackFrameProxy getFrameProxy() {
+ try {
+ return stepThread.frame(0);
+ }
+ catch (EvaluateException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ return null;
+ }
+ }
+
+ public DebugProcess getDebugProcess() {
+ return suspendContext.getDebugProcess();
+ }
+ });
+ }
+ });
+ }
+ catch (Exception e) {
+ myPosition = null;
+ }
+ }
+
+ public void setIgnoreFilters(boolean ignoreFilters) {
+ myIgnoreFilters = ignoreFilters;
+ }
+
+ public void setRestoreBreakpoints(boolean restoreBreakpoints) {
+ myRestoreBreakpoints = restoreBreakpoints;
+ }
+
+ public boolean isRestoreBreakpoints() {
+ return myRestoreBreakpoints;
+ }
+
+ public boolean isIgnoreFilters() {
+ return myIgnoreFilters;
+ }
+
+ public int getDepth() {
+ return mySkipThisMethod ? StepRequest.STEP_OUT : myDepth;
+ }
+
+ @Nullable
+ public SmartStepFilter getSmartStepFilter() {
+ return myTargetMethodSignature;
+ }
+
+ public int getNextStepDepth(final SuspendContextImpl context) {
+ try {
+ if ((myDepth == StepRequest.STEP_OVER || myDepth == StepRequest.STEP_INTO) && myPosition != null) {
+ final Integer resultDepth = ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+ public Integer compute() {
+ final SourcePosition locationPosition = ContextUtil.getSourcePosition(context);
+ if (locationPosition == null) {
+ return null;
+ }
+ int frameCount = -1;
+ final ThreadReferenceProxyImpl contextThread = context.getThread();
+ if (contextThread != null) {
+ try {
+ frameCount = contextThread.frameCount();
+ }
+ catch (EvaluateException e) {
+ }
+ }
+ final boolean filesEqual = myPosition.getFile().equals(locationPosition.getFile());
+ if (filesEqual && myPosition.getLine() == locationPosition.getLine() && myFrameCount == frameCount) {
+ return myDepth;
+ }
+ if (myDepth == StepRequest.STEP_INTO) {
+ if (filesEqual) {
+ // we are actually one or more frames upper than the original frame, should stop
+ if (myFrameCount > frameCount) {
+ return STOP;
+ }
+ // check if we are still at the line from which the stepping begun
+ if (myFrameCount == frameCount && myPosition.getLine() != locationPosition.getLine()) {
+ return STOP;
+ }
+ }
+ }
+ return null;
+ }
+ });
+ if (resultDepth != null) {
+ return resultDepth.intValue();
+ }
+ }
+ // the rest of the code makes sense for depth == STEP_INTO only
+
+ if (myDepth == StepRequest.STEP_INTO) {
+ final DebuggerSettings settings = DebuggerSettings.getInstance();
+ final StackFrameProxyImpl frameProxy = context.getFrameProxy();
+
+ if (settings.SKIP_SYNTHETIC_METHODS && frameProxy != null) {
+ final Location location = frameProxy.location();
+ final Method method = location.method();
+ if (method != null) {
+ if (myVirtualMachineProxy.canGetSyntheticAttribute()? method.isSynthetic() : method.name().indexOf('$') >= 0) {
+ return myDepth;
+ }
+ }
+ }
+
+ if (!myIgnoreFilters) {
+ if(settings.SKIP_GETTERS) {
+ boolean isGetter = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>(){
+ public Boolean compute() {
+ final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(PositionUtil.getContextElement(context), PsiMethod.class);
+ return (psiMethod != null && DebuggerUtils.isSimpleGetter(psiMethod))? Boolean.TRUE : Boolean.FALSE;
+ }
+ }).booleanValue();
+
+ if(isGetter) {
+ return StepRequest.STEP_OUT;
+ }
+ }
+
+ if (frameProxy != null) {
+ if (settings.SKIP_CONSTRUCTORS) {
+ final Location location = frameProxy.location();
+ final Method method = location.method();
+ if (method != null && method.isConstructor()) {
+ return StepRequest.STEP_OUT;
+ }
+ }
+
+ if (settings.SKIP_CLASSLOADERS) {
+ final Location location = frameProxy.location();
+ if (DebuggerUtilsEx.isAssignableFrom("java.lang.ClassLoader", location.declaringType())) {
+ return StepRequest.STEP_OUT;
+ }
+ }
+ }
+ }
+ // smart step feature
+ if (myTargetMethodSignature != null) {
+ if (!myTargetMethodSignature.shouldStopAtLocation(context)) {
+ return StepRequest.STEP_OUT;
+ }
+ }
+ }
+ }
+ catch (VMDisconnectedException e) {
+ }
+ catch (EvaluateException e) {
+ LOG.error(e);
+ }
+ return STOP;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java
new file mode 100644
index 0000000..4ce8162
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.Patches;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.HashSet;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.event.EventSet;
+import com.sun.jdi.request.EventRequest;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * @author lex
+ */
+public abstract class SuspendContextImpl implements SuspendContext {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendContextImpl");
+
+ private final DebugProcessImpl myDebugProcess;
+ private final int mySuspendPolicy;
+
+ private ThreadReferenceProxyImpl myThread;
+ boolean myIsVotedForResume = true;
+
+ protected int myVotesToVote;
+ protected Set<ThreadReferenceProxyImpl> myResumedThreads;
+
+ private final EventSet myEventSet;
+ private volatile boolean myIsResumed;
+
+ public ConcurrentLinkedQueue<SuspendContextCommandImpl> myPostponedCommands = new ConcurrentLinkedQueue<SuspendContextCommandImpl>();
+ public volatile boolean myInProgress;
+ private final HashSet<ObjectReference> myKeptReferences = new HashSet<ObjectReference>();
+ private EvaluationContextImpl myEvaluationContext = null;
+
+ SuspendContextImpl(@NotNull DebugProcessImpl debugProcess, int suspendPolicy, int eventVotes, EventSet set) {
+ myDebugProcess = debugProcess;
+ mySuspendPolicy = suspendPolicy;
+ myVotesToVote = eventVotes;
+ myEventSet = set;
+ }
+
+ public void setThread(ThreadReference thread) {
+ assertNotResumed();
+ ThreadReferenceProxyImpl threadProxy = myDebugProcess.getVirtualMachineProxy().getThreadReferenceProxy(thread);
+ LOG.assertTrue(myThread == null || myThread == threadProxy);
+ myThread = threadProxy;
+ }
+
+ protected abstract void resumeImpl();
+
+ protected void resume(){
+ assertNotResumed();
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
+ for (ObjectReference objectReference : myKeptReferences) {
+ try {
+ objectReference.enableCollection();
+ }
+ catch (UnsupportedOperationException e) {
+ // ignore: some J2ME implementations does not provide this operation
+ }
+ }
+ myKeptReferences.clear();
+ }
+
+ for(SuspendContextCommandImpl cmd = pollPostponedCommand(); cmd != null; cmd = pollPostponedCommand()) {
+ cmd.notifyCancelled();
+ }
+
+ resumeImpl();
+ }
+ finally {
+ myIsResumed = true;
+ }
+ }
+
+ private void assertNotResumed() {
+ if (myIsResumed) {
+ if (myDebugProcess.isAttached()) {
+ LOG.error("Cannot access SuspendContext. SuspendContext is resumed.");
+ }
+ }
+ }
+
+
+ public EventSet getEventSet() {
+ assertNotResumed();
+ return myEventSet;
+ }
+
+ public DebugProcessImpl getDebugProcess() {
+ assertNotResumed();
+ return myDebugProcess;
+ }
+
+ public StackFrameProxyImpl getFrameProxy() {
+ assertNotResumed();
+ try {
+ return myThread != null && myThread.frameCount() > 0 ? myThread.frame(0) : null;
+ }
+ catch (EvaluateException e) {
+ return null;
+ }
+ }
+
+ public ThreadReferenceProxyImpl getThread() {
+ return myThread;
+ }
+
+ public int getSuspendPolicy() {
+ assertNotResumed();
+ return mySuspendPolicy;
+ }
+
+ public void doNotResumeHack() {
+ assertNotResumed();
+ myVotesToVote = 1000000000;
+ }
+
+ public boolean isExplicitlyResumed(ThreadReferenceProxyImpl thread) {
+ return myResumedThreads != null ? myResumedThreads.contains(thread) : false;
+ }
+
+ public boolean suspends(ThreadReferenceProxyImpl thread) {
+ assertNotResumed();
+ if(isEvaluating()) {
+ return false;
+ }
+ switch(getSuspendPolicy()) {
+ case EventRequest.SUSPEND_ALL:
+ return !isExplicitlyResumed(thread);
+ case EventRequest.SUSPEND_EVENT_THREAD:
+ return thread == getThread();
+ }
+ return false;
+ }
+
+ public boolean isEvaluating() {
+ assertNotResumed();
+ return myEvaluationContext != null;
+ }
+
+ public EvaluationContextImpl getEvaluationContext() {
+ return myEvaluationContext;
+ }
+
+ public boolean isResumed() {
+ return myIsResumed;
+ }
+
+ public void setIsEvaluating(EvaluationContextImpl evaluationContext) {
+ assertNotResumed();
+ myEvaluationContext = evaluationContext;
+ }
+
+ public String toString() {
+ if (myEventSet != null) {
+ return myEventSet.toString();
+ }
+ return myThread != null ? myThread.toString() : DebuggerBundle.message("string.null.context");
+ }
+
+ public void keep(ObjectReference reference) {
+ if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
+ final boolean added = myKeptReferences.add(reference);
+ if (added) {
+ try {
+ reference.disableCollection();
+ }
+ catch (UnsupportedOperationException e) {
+ // ignore: some J2ME implementations does not provide this operation
+ }
+ }
+ }
+ }
+
+ public final void postponeCommand(final SuspendContextCommandImpl command) {
+ if (!isResumed()) {
+ // Important! when postponing increment the holds counter, so that the action is not released too early.
+ // This will ensure that the counter becomes zero only when the command is actually executed or canceled
+ command.hold();
+ myPostponedCommands.add(command);
+ }
+ else {
+ command.notifyCancelled();
+ }
+ }
+
+ public final SuspendContextCommandImpl pollPostponedCommand() {
+ return myPostponedCommands.poll();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextRunnable.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextRunnable.java
new file mode 100644
index 0000000..6df6a14
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextRunnable.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.engine.SuspendContextImpl;
+
+public interface SuspendContextRunnable {
+ void run(SuspendContextImpl suspendContext) throws Exception;
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManager.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManager.java
new file mode 100644
index 0000000..92fdd71
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.event.EventSet;
+
+import java.util.List;
+
+public interface SuspendManager {
+ SuspendContextImpl pushSuspendContext(EventSet eventSet);
+ SuspendContextImpl pushSuspendContext(int suspendAll, int i);
+
+ void resume(SuspendContextImpl suspendContext);
+
+ //replaces current context with new one at the same location and fires 'paused' event
+ void popFrame(SuspendContextImpl suspendContext);
+
+ SuspendContextImpl getPausedContext();
+ boolean isFrozen(ThreadReferenceProxyImpl thread);
+ boolean isSuspended(ThreadReferenceProxyImpl thread) throws ObjectCollectedException;
+
+ void freezeThread(ThreadReferenceProxyImpl invokeThread);
+ void unfreezeThread(ThreadReferenceProxyImpl thread);
+
+ void resumeThread(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl invokeThread);
+ void suspendThread(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl invokeThread);
+
+ void voteResume(SuspendContextImpl suspendContext);
+ void voteSuspend(SuspendContextImpl suspendContext);
+
+ List<SuspendContextImpl> getEventContexts();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java
new file mode 100644
index 0000000..de161f1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.InternalException;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.event.EventSet;
+import com.sun.jdi.request.EventRequest;
+
+import java.util.*;
+
+/**
+ * @author lex
+ */
+public class SuspendManagerImpl implements SuspendManager {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendManager");
+
+ private final LinkedList<SuspendContextImpl> myEventContexts = new LinkedList<SuspendContextImpl>();
+ /**
+ * contexts, paused at breakpoint or another debugger event requests. Note that thread, explicitly paused by user is not considered as
+ * "paused at breakpoint" and JDI prohibits data queries on its stackframes
+ */
+ private final LinkedList<SuspendContextImpl> myPausedContexts = new LinkedList<SuspendContextImpl>();
+ private final Set<ThreadReferenceProxyImpl> myFrozenThreads = Collections.synchronizedSet(new HashSet<ThreadReferenceProxyImpl>());
+
+ private final DebugProcessImpl myDebugProcess;
+
+ public int suspends = 0;
+
+ public SuspendManagerImpl(DebugProcessImpl debugProcess) {
+ myDebugProcess = debugProcess;
+ myDebugProcess.addDebugProcessListener(new DebugProcessAdapterImpl() {
+ public void processDetached(DebugProcessImpl process, boolean closedByUser) {
+ myEventContexts.clear();
+ myPausedContexts.clear();
+ myFrozenThreads.clear();
+ }
+ });
+ }
+
+ public SuspendContextImpl pushSuspendContext(final int suspendPolicy, int nVotes) {
+ SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, suspendPolicy, nVotes, null) {
+ protected void resumeImpl() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Start resuming...");
+ }
+ myDebugProcess.logThreads();
+ switch(getSuspendPolicy()) {
+ case EventRequest.SUSPEND_ALL:
+ int resumeAttempts = 5;
+ while (--resumeAttempts > 0) {
+ try {
+ myDebugProcess.getVirtualMachineProxy().resume();
+ break;
+ }
+ catch (InternalException e) {
+ //InternalException 13 means that there are running threads that we are trying to resume
+ //On MacOS it happened that native thread didn't stop while some java thread reached breakpoint
+ if (/*Patches.MAC_RESUME_VM_HACK && */e.errorCode() == 13) {
+ //Its funny, but second resume solves the problem
+ continue;
+ }
+ else {
+ LOG.error(e);
+ break;
+ }
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("VM resumed ");
+ }
+ break;
+ case EventRequest.SUSPEND_EVENT_THREAD:
+ getThread().resume();
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Thread resumed : " + getThread().toString());
+ }
+ break;
+ case EventRequest.SUSPEND_NONE:
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("None resumed");
+ }
+ break;
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Suspends = " + suspends);
+ }
+ myDebugProcess.logThreads();
+ }
+ };
+ pushContext(suspendContext);
+ return suspendContext;
+ }
+
+ public SuspendContextImpl pushSuspendContext(final EventSet set) {
+ SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, set.suspendPolicy(), set.size(), set) {
+ protected void resumeImpl() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Start resuming eventSet " + set.toString() + " suspendPolicy = " + set.suspendPolicy() + ",size = " + set.size());
+ }
+ myDebugProcess.logThreads();
+ //final ThreadReferenceProxyImpl thread = getThread();
+ //
+ //if (thread != null) { // check that thread is suspended at the moment
+ // try {
+ // if (!thread.isSuspended()) {
+ // final int status = thread.status();
+ // if ((status != ThreadReference.THREAD_STATUS_ZOMBIE) && (status != ThreadReference.THREAD_STATUS_NOT_STARTED) && (status != ThreadReference.THREAD_STATUS_UNKNOWN)) {
+ // LOG.error("Context thread must be suspended");
+ // }
+ // }
+ // }
+ // catch (ObjectCollectedException ignored) {}
+ //}
+
+ int attempts = 5;
+ while (--attempts > 0) {
+ try {
+ set.resume();
+ break;
+ }
+ catch (ObjectCollectedException e) {
+ // according to error reports set.resume() may throw this if one of the threads has been collected
+ LOG.info(e);
+ continue;
+ }
+ catch (InternalException e) {
+ //InternalException 13 means that there are running threads that we are trying to resume
+ //On MacOS it happened that native thread didn't stop while some java thread reached breakpoint
+ if (/*Patches.MAC_RESUME_VM_HACK && */e.errorCode() == 13 && set.suspendPolicy() == EventRequest.SUSPEND_ALL) {
+ //Its funny, but second resume solves the problem
+ continue;
+ }
+ else {
+ LOG.error(e);
+ break;
+ }
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Set resumed ");
+ }
+ myDebugProcess.logThreads();
+ }
+ };
+ pushContext(suspendContext);
+ return suspendContext;
+ }
+
+ private void pushContext(SuspendContextImpl suspendContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myEventContexts.addFirst(suspendContext);
+ suspends++;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Push context : Suspends = " + suspends);
+ }
+ }
+
+ public void resume(SuspendContextImpl context) {
+ SuspendManagerUtil.prepareForResume(context);
+
+ myDebugProcess.logThreads();
+ final int suspendPolicy = context.getSuspendPolicy();
+ popContext(context);
+ context.resume();
+ myDebugProcess.clearCashes(suspendPolicy);
+ }
+
+ public void popFrame(SuspendContextImpl suspendContext) {
+ popContext(suspendContext);
+ SuspendContextImpl newSuspendContext = pushSuspendContext(suspendContext.getSuspendPolicy(), 0);
+ newSuspendContext.setThread(suspendContext.getThread().getThreadReference());
+ notifyPaused(newSuspendContext);
+ }
+
+ public SuspendContextImpl getPausedContext() {
+ return !myPausedContexts.isEmpty() ? myPausedContexts.getFirst() : null;
+ }
+
+ public void popContext(SuspendContextImpl suspendContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ suspends--;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("popContext, suspends = " + suspends);
+ }
+ myEventContexts.remove(suspendContext);
+ myPausedContexts.remove(suspendContext);
+ }
+
+ void pushPausedContext(SuspendContextImpl suspendContext) {
+ if(LOG.isDebugEnabled()) {
+ LOG.assertTrue(myEventContexts.contains(suspendContext));
+ }
+
+ myPausedContexts.addFirst(suspendContext);
+ }
+
+ public boolean hasEventContext(SuspendContextImpl suspendContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return myEventContexts.contains(suspendContext);
+ }
+
+ public List<SuspendContextImpl> getEventContexts() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return Collections.unmodifiableList(myEventContexts);
+ }
+
+ public boolean isFrozen(ThreadReferenceProxyImpl thread) {
+ return myFrozenThreads.contains(thread);
+ }
+
+ public boolean isSuspended(ThreadReferenceProxyImpl thread) throws ObjectCollectedException{
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ boolean suspended = false;
+
+ if (isFrozen(thread)) {
+ suspended = true;
+ }
+ else {
+ for (SuspendContextImpl suspendContext : myEventContexts) {
+ if (suspendContext.suspends(thread)) {
+ suspended = true;
+ break;
+ }
+ }
+ }
+
+ //bug in JDI : newly created thread may be resumed even when suspendPolicy == SUSPEND_ALL
+ //if(LOG.isDebugEnabled() && suspended) {
+ // LOG.assertTrue(thread.suspends(), thread.name());
+ //}
+ return suspended && (thread == null || thread.isSuspended());
+ }
+
+ public void suspendThread(SuspendContextImpl context, ThreadReferenceProxyImpl thread) {
+ LOG.assertTrue(thread != context.getThread(), "Thread is already suspended at the breakpoint");
+
+ if(context.isExplicitlyResumed(thread)) {
+ context.myResumedThreads.remove(thread);
+ thread.suspend();
+ }
+ }
+
+ public void resumeThread(SuspendContextImpl context, ThreadReferenceProxyImpl thread) {
+ LOG.assertTrue(thread != context.getThread(), "Use resume() instead of resuming breakpoint thread");
+ LOG.assertTrue(!context.isExplicitlyResumed(thread));
+
+ if(context.myResumedThreads == null) {
+ context.myResumedThreads = new HashSet<ThreadReferenceProxyImpl>();
+ }
+ context.myResumedThreads.add(thread);
+ thread.resume();
+ }
+
+ public void freezeThread(ThreadReferenceProxyImpl thread) {
+ if (myFrozenThreads.add(thread)) {
+ thread.suspend();
+ }
+ }
+
+ public void unfreezeThread(ThreadReferenceProxyImpl thread) {
+ if (myFrozenThreads.remove(thread)) {
+ thread.resume();
+ }
+ }
+
+ private void processVote(SuspendContextImpl suspendContext) {
+ LOG.assertTrue(suspendContext.myVotesToVote > 0);
+ suspendContext.myVotesToVote--;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("myVotesToVote = " + suspendContext.myVotesToVote);
+ }
+ if(suspendContext.myVotesToVote == 0) {
+ if(suspendContext.myIsVotedForResume) {
+ resume(suspendContext);
+ }
+ else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("vote paused");
+ }
+ myDebugProcess.logThreads();
+ myDebugProcess.cancelRunToCursorBreakpoint();
+ final ThreadReferenceProxyImpl thread = suspendContext.getThread();
+ myDebugProcess.deleteStepRequests(thread != null? thread.getThreadReference() : null);
+ notifyPaused(suspendContext);
+ }
+ }
+ }
+
+ public void notifyPaused(SuspendContextImpl suspendContext) {
+ pushPausedContext(suspendContext);
+ myDebugProcess.myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
+ }
+
+ public void voteResume(SuspendContextImpl suspendContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Resume voted");
+ }
+ processVote(suspendContext);
+ }
+
+ public void voteSuspend(SuspendContextImpl suspendContext) {
+ suspendContext.myIsVotedForResume = false;
+ processVote(suspendContext);
+ }
+
+ LinkedList<SuspendContextImpl> getPausedContexts() {
+ return myPausedContexts;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java
new file mode 100644
index 0000000..e53f9d5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Set;
+
+public class SuspendManagerUtil {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendManagerUtil");
+
+ public static boolean isEvaluating(SuspendManager suspendManager, ThreadReferenceProxyImpl thread) {
+ for (SuspendContextImpl suspendContext : suspendManager.getEventContexts()) {
+ if (suspendContext.isEvaluating() && thread.equals(suspendContext.getThread())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static SuspendContextImpl findContextByThread(SuspendManager suspendManager, ThreadReferenceProxyImpl thread) {
+ for (ListIterator<SuspendContextImpl> iterator = ((SuspendManagerImpl) suspendManager).getPausedContexts().listIterator(); iterator.hasNext();) {
+ SuspendContextImpl context = iterator.next();
+ if(context.getThread() == thread) {
+ return context;
+ }
+ }
+
+ return null;
+ }
+
+ public static void assertSuspendContext(SuspendContextImpl context) {
+ if(LOG.isDebugEnabled()) {
+ LOG.assertTrue(context.myInProgress, "You can invoke methods only inside commands invoked for SuspendContext");
+ }
+ }
+
+ public static Set<SuspendContextImpl> getSuspendingContexts(SuspendManager suspendManager, ThreadReferenceProxyImpl thread) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final Set<SuspendContextImpl> result = new HashSet<SuspendContextImpl>();
+ for (final SuspendContextImpl suspendContext : suspendManager.getEventContexts()) {
+ if (suspendContext.suspends(thread)) {
+ result.add(suspendContext);
+ }
+ }
+ return result;
+ }
+
+ public static void restoreAfterResume(SuspendContextImpl context, Object resumeData) {
+ SuspendManager suspendManager = context.getDebugProcess().getSuspendManager();
+ ResumeData data = (ResumeData) resumeData;
+
+ ThreadReferenceProxyImpl thread = context.getThread();
+ if(data.myIsFrozen && !suspendManager.isFrozen(thread)) {
+ suspendManager.freezeThread(thread);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("RestoreAfterResume SuspendContextImpl...");
+ }
+ LOG.assertTrue(context.myResumedThreads == null);
+
+ if(data.myResumedThreads != null) {
+ for (Iterator<ThreadReferenceProxyImpl> iterator = data.myResumedThreads.iterator(); iterator.hasNext();) {
+ ThreadReferenceProxyImpl resumedThreads = iterator.next();
+ resumedThreads.resume();
+ }
+ context.myResumedThreads = data.myResumedThreads;
+ }
+ }
+
+ public static Object prepareForResume(SuspendContextImpl context) {
+ SuspendManager suspendManager = context.getDebugProcess().getSuspendManager();
+
+ ThreadReferenceProxyImpl thread = context.getThread();
+
+ ResumeData resumeData = new ResumeData(suspendManager.isFrozen(thread), context.myResumedThreads);
+
+ if(resumeData.myIsFrozen) {
+ suspendManager.unfreezeThread(thread);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Resuming SuspendContextImpl...");
+ }
+ if(context.myResumedThreads != null) {
+ for (Iterator<ThreadReferenceProxyImpl> iterator = context.myResumedThreads.iterator(); iterator.hasNext();) {
+ ThreadReferenceProxyImpl resumedThreads = iterator.next();
+ resumedThreads.suspend();
+ }
+ context.myResumedThreads = null;
+ }
+
+ return resumeData;
+ }
+
+ public static SuspendContextImpl getSuspendContextForThread(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
+ if (suspendContext == null) {
+ return null;
+ }
+ SuspendContextImpl context = findContextByThread(suspendContext.getDebugProcess().getSuspendManager(), thread);
+ return context != null && !context.myInProgress ? context : suspendContext;
+ }
+
+ public static SuspendContextImpl getEvaluatingContext(SuspendManager suspendManager, ThreadReferenceProxyImpl thread) {
+ for (SuspendContextImpl suspendContext : suspendManager.getEventContexts()) {
+ if (!suspendContext.isResumed() && suspendContext.isEvaluating() && suspendContext.getThread() == thread) {
+ return suspendContext;
+ }
+ }
+ return null;
+ }
+
+ private static class ResumeData {
+ final boolean myIsFrozen;
+ final Set<ThreadReferenceProxyImpl> myResumedThreads;
+
+ public ResumeData(boolean isFrozen, Set<ThreadReferenceProxyImpl> resumedThreads) {
+ myIsFrozen = isFrozen;
+ myResumedThreads = resumedThreads;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/VMEventListener.java b/java/debugger/impl/src/com/intellij/debugger/engine/VMEventListener.java
new file mode 100644
index 0000000..9f6ad28
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/VMEventListener.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jul 15, 2003
+ * Time: 6:38:23 PM
+ * To change this template use Options | File Templates.
+ */
+public interface VMEventListener {
+ //aware! called in DebuggerEventThread
+ void vmEvent(com.sun.jdi.event.Event event);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java
new file mode 100644
index 0000000..4249138
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2000-2010 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.JavaCodeFragment;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.Value;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Aug 30, 2010
+ */
+public class CodeFragmentFactoryContextWrapper extends CodeFragmentFactory {
+ public static final Key<Value> LABEL_VARIABLE_VALUE_KEY = Key.create("_label_variable_value_key_");
+ public static final String DEBUG_LABEL_SUFFIX = "_DebugLabel";
+
+ private final CodeFragmentFactory myDelegate;
+
+ public CodeFragmentFactoryContextWrapper(CodeFragmentFactory delegate) {
+ myDelegate = delegate;
+ }
+
+ public JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, Project project) {
+ return myDelegate.createCodeFragment(item, wrapContext(project, context), project);
+ }
+
+ public JavaCodeFragment createPresentationCodeFragment(TextWithImports item, PsiElement context, Project project) {
+ return myDelegate.createPresentationCodeFragment(item, wrapContext(project, context), project);
+ }
+
+ public boolean isContextAccepted(PsiElement contextElement) {
+ return myDelegate.isContextAccepted(contextElement);
+ }
+
+ public LanguageFileType getFileType() {
+ return myDelegate.getFileType();
+ }
+
+ @Override
+ public EvaluatorBuilder getEvaluatorBuilder() {
+ return myDelegate.getEvaluatorBuilder();
+ }
+
+ private PsiElement wrapContext(Project project, final PsiElement originalContext) {
+ if (project.isDefault()) return originalContext;
+ PsiElement context = originalContext;
+ final DebugProcessImpl process = DebuggerManagerEx.getInstanceEx(project).getContext().getDebugProcess();
+ if (process != null) {
+ final Map<ObjectReference, ValueMarkup> markupMap = ValueDescriptorImpl.getMarkupMap(process);
+ if (markupMap != null && markupMap.size() > 0) {
+ final Pair<String, Map<String, ObjectReference>> markupVariables = createMarkupVariablesText(markupMap);
+ int offset = markupVariables.getFirst().length() - 1;
+ final TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, markupVariables.getFirst(), "", myDelegate.getFileType());
+ final JavaCodeFragment codeFragment = myDelegate.createCodeFragment(textWithImports, context, project);
+ codeFragment.accept(new JavaRecursiveElementVisitor() {
+ public void visitLocalVariable(PsiLocalVariable variable) {
+ final String name = variable.getName();
+ variable.putUserData(LABEL_VARIABLE_VALUE_KEY, markupVariables.getSecond().get(name));
+ }
+ });
+ final PsiElement newContext = codeFragment.findElementAt(offset);
+ if (newContext != null) {
+ context = newContext;
+ }
+ }
+ }
+ return context;
+ }
+
+ private static Pair<String, Map<String, ObjectReference>> createMarkupVariablesText(Map<ObjectReference, ValueMarkup> markupMap) {
+ final Map<String, ObjectReference> reverseMap = new HashMap<String, ObjectReference>();
+ final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ for (Iterator<Map.Entry<ObjectReference, ValueMarkup>> it = markupMap.entrySet().iterator(); it.hasNext();) {
+ Map.Entry<ObjectReference, ValueMarkup> entry = it.next();
+ final ObjectReference objectRef = entry.getKey();
+ final ValueMarkup markup = entry.getValue();
+ String labelName = markup.getText();
+ if (!StringUtil.isJavaIdentifier(labelName)) {
+ continue;
+ }
+ try {
+ final String typeName = objectRef.type().name();
+ labelName += DEBUG_LABEL_SUFFIX;
+ if (buffer.length() > 0) {
+ buffer.append("\n");
+ }
+ buffer.append(typeName).append(" ").append(labelName).append(";");
+ reverseMap.put(labelName, objectRef);
+ }
+ catch (ObjectCollectedException e) {
+ it.remove();
+ }
+ }
+ buffer.append(" ");
+ return new Pair<String, Map<String, ObjectReference>>(buffer.toString(), reverseMap);
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerHighlightFilter.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerHighlightFilter.java
new file mode 100644
index 0000000..985b3ed
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerHighlightFilter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.codeInsight.daemon.impl.HighlightInfo;
+import com.intellij.codeInsight.daemon.impl.HighlightInfoFilter;
+import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+public class DebuggerHighlightFilter implements HighlightInfoFilter {
+ public boolean accept(@NotNull HighlightInfo highlightInfo, PsiFile file) {
+ return highlightInfo.type != HighlightInfoType.UNHANDLED_EXCEPTION ||
+ file == null ||
+ file.getUserData(DebuggerExpressionComboBox.KEY) == null;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java
new file mode 100644
index 0000000..15ff6b9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.codeInsight.completion.CompletionParameters;
+import com.intellij.codeInsight.completion.CompletionService;
+import com.intellij.codeInsight.completion.JavaCompletionUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.codeinsight.RuntimeTypeEvaluator;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.util.PairFunction;
+import com.intellij.util.concurrency.Semaphore;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jun 7, 2005
+ */
+public class DefaultCodeFragmentFactory extends CodeFragmentFactory {
+ private static final class SingletonHolder {
+ public static final DefaultCodeFragmentFactory ourInstance = new DefaultCodeFragmentFactory();
+ }
+
+ public static DefaultCodeFragmentFactory getInstance() {
+ return SingletonHolder.ourInstance;
+ }
+
+ public JavaCodeFragment createPresentationCodeFragment(final TextWithImports item, final PsiElement context, final Project project) {
+ return createCodeFragment(item, context, project);
+ }
+
+ public JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, final Project project) {
+ final JavaCodeFragmentFactory factory = JavaCodeFragmentFactory.getInstance(project);
+ final String text = item.getText();
+
+ final JavaCodeFragment fragment;
+ if (CodeFragmentKind.EXPRESSION == item.getKind()) {
+ final String expressionText = StringUtil.endsWithChar(text, ';')? text.substring(0, text.length() - 1) : text;
+ fragment = factory.createExpressionCodeFragment(expressionText, context, null, true);
+ }
+ else /*if (CodeFragmentKind.CODE_BLOCK == item.getKind())*/ {
+ fragment = factory.createCodeBlockCodeFragment(text, context, true);
+ }
+
+ if(item.getImports().length() > 0) {
+ fragment.addImportsFromString(item.getImports());
+ }
+ fragment.setVisibilityChecker(JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE);
+ //noinspection HardCodedStringLiteral
+ fragment.putUserData(DebuggerExpressionComboBox.KEY, "DebuggerComboBoxEditor.IS_DEBUGGER_EDITOR");
+ fragment.putCopyableUserData(JavaCompletionUtil.DYNAMIC_TYPE_EVALUATOR, new PairFunction<PsiExpression, CompletionParameters, PsiType>() {
+ public PsiType fun(PsiExpression expression, CompletionParameters parameters) {
+ if (!RuntimeTypeEvaluator.isSubtypeable(expression)) {
+ return null;
+ }
+
+ if (parameters.getInvocationCount() <= 1 && JavaCompletionUtil.mayHaveSideEffects(expression)) {
+ final CompletionService service = CompletionService.getCompletionService();
+ if (parameters.getInvocationCount() < 2) {
+ service.setAdvertisementText("Invoke completion once more to see runtime type variants");
+ }
+ return null;
+ }
+
+ final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext();
+ DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
+ if (debuggerSession != null) {
+ final Semaphore semaphore = new Semaphore();
+ semaphore.down();
+ final AtomicReference<PsiClass> nameRef = new AtomicReference<PsiClass>();
+ final RuntimeTypeEvaluator worker =
+ new RuntimeTypeEvaluator(null, expression, debuggerContext, ProgressManager.getInstance().getProgressIndicator()) {
+ @Override
+ protected void typeCalculationFinished(@Nullable PsiClass type) {
+ nameRef.set(type);
+ semaphore.up();
+ }
+ };
+ debuggerContext.getDebugProcess().getManagerThread().invoke(worker);
+ for (int i = 0; i < 50; i++) {
+ ProgressManager.checkCanceled();
+ if (semaphore.waitFor(20)) break;
+ }
+ final PsiClass psiClass = nameRef.get();
+ if (psiClass != null) {
+ return JavaPsiFacade.getElementFactory(project).createType(psiClass);
+ }
+ }
+ return null;
+ }
+ });
+
+ return fragment;
+ }
+
+ public boolean isContextAccepted(PsiElement contextElement) {
+ return true; // default factory works everywhere debugger can stop
+ }
+
+ public LanguageFileType getFileType() {
+ return StdFileTypes.JAVA;
+ }
+
+ @Override
+ public EvaluatorBuilder getEvaluatorBuilder() {
+ return EvaluatorBuilderImpl.getInstance();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluateRuntimeException.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluateRuntimeException.java
new file mode 100644
index 0000000..a5b460c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluateRuntimeException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+/**
+ * @author lex
+ */
+public class EvaluateRuntimeException extends RuntimeException {
+ public EvaluateRuntimeException(EvaluateException e) {
+ super(e);
+ }
+
+ public EvaluateException getCause() {
+ return (EvaluateException)super.getCause();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java
new file mode 100644
index 0000000..24babfd
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ClassLoaderReference;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: lex
+ * Date: Aug 28, 2003
+ * Time: 2:02:29 PM
+ */
+public final class EvaluationContextImpl implements EvaluationContext{
+ private final Value myThisObject;
+ private final SuspendContextImpl mySuspendContext;
+ private final StackFrameProxyImpl myFrameProxy;
+ private boolean myAutoLoadClasses = true;
+
+ public EvaluationContextImpl(@NotNull SuspendContextImpl suspendContext, StackFrameProxyImpl frameProxy, Value thisObject) {
+ myThisObject = thisObject;
+ myFrameProxy = frameProxy;
+ mySuspendContext = suspendContext;
+ }
+
+ public Value getThisObject() {
+ return myThisObject;
+ }
+
+ public SuspendContextImpl getSuspendContext() {
+ return mySuspendContext;
+ }
+
+ public StackFrameProxyImpl getFrameProxy() {
+ return myFrameProxy;
+ }
+
+ public DebugProcessImpl getDebugProcess() {
+ return getSuspendContext().getDebugProcess();
+ }
+
+ public Project getProject() {
+ DebugProcessImpl debugProcess = getDebugProcess();
+ return debugProcess != null ? debugProcess.getProject() : null;
+ }
+
+ public EvaluationContextImpl createEvaluationContext(Value value) {
+ final EvaluationContextImpl copy = new EvaluationContextImpl(getSuspendContext(), getFrameProxy(), value);
+ copy.setAutoLoadClasses(myAutoLoadClasses);
+ return copy;
+ }
+
+ public ClassLoaderReference getClassLoader() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return myFrameProxy != null ? myFrameProxy.getClassLoader() : null;
+ }
+
+ public boolean isAutoLoadClasses() {
+ return myAutoLoadClasses;
+ }
+
+ public void setAutoLoadClasses(final boolean autoLoadClasses) {
+ myAutoLoadClasses = autoLoadClasses;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationListener.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationListener.java
new file mode 100644
index 0000000..a9bfa83
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.debugger.engine.SuspendContextImpl;
+
+import java.util.EventListener;
+
+/**
+ * @author lex
+ */
+public interface EvaluationListener extends EventListener {
+ void evaluationStarted(SuspendContextImpl context);
+
+ void evaluationFinished(SuspendContextImpl context);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/TextWithImportsImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/TextWithImportsImpl.java
new file mode 100644
index 0000000..4eda898
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/TextWithImportsImpl.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.debugger.ui.DebuggerEditorImpl;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Trinity;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.JavaCodeFragment;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionCodeFragment;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public final class TextWithImportsImpl implements TextWithImports{
+
+ private final CodeFragmentKind myKind;
+ private String myText;
+ private final FileType myFileType;
+ private final String myImports;
+
+ public TextWithImportsImpl (PsiExpression expression) {
+ myKind = CodeFragmentKind.EXPRESSION;
+ final String text = expression.getText();
+ PsiFile containingFile = expression.getContainingFile();
+ if(containingFile instanceof PsiExpressionCodeFragment) {
+ myText = text;
+ myImports = ((JavaCodeFragment)containingFile).importsToString();
+ myFileType = StdFileTypes.JAVA;
+ }
+ else {
+ Trinity<String, String, FileType> trinity = parseExternalForm(text);
+ myText = trinity.first;
+ myImports = trinity.second;
+ myFileType = trinity.third;
+ }
+ }
+
+ public TextWithImportsImpl (CodeFragmentKind kind, @NotNull String text, @NotNull String imports, @Nullable FileType fileType) {
+ myKind = kind;
+ myText = text;
+ myImports = imports;
+ myFileType = fileType;
+ }
+
+ public TextWithImportsImpl(CodeFragmentKind kind, @NotNull String text) {
+ myKind = kind;
+ Trinity<String, String, FileType> trinity = parseExternalForm(text);
+ myText = trinity.first;
+ myImports = trinity.second;
+ myFileType = trinity.third;
+ }
+
+ private static Trinity<String, String, FileType> parseExternalForm(String s) {
+ String[] split = s.split(String.valueOf(DebuggerEditorImpl.SEPARATOR));
+ return Trinity.create(split[0], split.length > 1 ? split[1] : "", split.length > 2 ? FileTypeManager.getInstance().getStdFileType(split[2]) : null);
+ }
+
+ public CodeFragmentKind getKind() {
+ return myKind;
+ }
+
+ public String getText() {
+ return myText;
+ }
+
+ public @NotNull String getImports() {
+ return myImports;
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof TextWithImportsImpl)) {
+ return false;
+ }
+ TextWithImportsImpl item = ((TextWithImportsImpl)object);
+ return Comparing.equal(item.myText, myText) && Comparing.equal(item.myImports, myImports);
+ }
+
+ public String toString() {
+ return getText();
+ }
+
+ public String toExternalForm() {
+ String result = myText;
+ if (StringUtil.isNotEmpty(myImports) || myFileType != null) {
+ result += DebuggerEditorImpl.SEPARATOR + myImports;
+ }
+ if (myFileType != null) {
+ result += DebuggerEditorImpl.SEPARATOR + myFileType.getName();
+ }
+ return result;
+ }
+
+ public int hashCode() {
+ return myText.hashCode();
+ }
+
+ public boolean isEmpty() {
+ final String text = getText();
+ return text == null || "".equals(text.trim());
+ }
+
+ public void setText(String newText) {
+ myText = newText;
+ }
+
+ public FileType getFileType() {
+ return myFileType;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ArrayAccessEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ArrayAccessEvaluator.java
new file mode 100644
index 0000000..4413c83
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ArrayAccessEvaluator.java
@@ -0,0 +1,98 @@
+/*
+ * 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 ArrayAccessEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.*;
+
+class ArrayAccessEvaluator implements Evaluator {
+ private final Evaluator myArrayReferenceEvaluator;
+ private final Evaluator myIndexEvaluator;
+ private ArrayReference myEvaluatedArrayReference;
+ private int myEvaluatedIndex;
+
+ public ArrayAccessEvaluator(Evaluator arrayReferenceEvaluator, Evaluator indexEvaluator) {
+ myArrayReferenceEvaluator = arrayReferenceEvaluator;
+ myIndexEvaluator = indexEvaluator;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ myEvaluatedIndex = 0;
+ myEvaluatedArrayReference = null;
+ Value indexValue = (Value)myIndexEvaluator.evaluate(context);
+ Value arrayValue = (Value)myArrayReferenceEvaluator.evaluate(context);
+ if (!(arrayValue instanceof ArrayReference)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.array.reference.expected"));
+ }
+ myEvaluatedArrayReference = (ArrayReference)arrayValue;
+ if (!DebuggerUtilsEx.isInteger(indexValue)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.index.expression"));
+ }
+ myEvaluatedIndex = ((PrimitiveValue)indexValue).intValue();
+ try {
+ return myEvaluatedArrayReference.getValue(myEvaluatedIndex);
+ }
+ catch (Exception e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ public Modifier getModifier() {
+ Modifier modifier = null;
+ if (myEvaluatedArrayReference != null) {
+ modifier = new Modifier() {
+ public boolean canInspect() {
+ return true;
+ }
+
+ public boolean canSetValue() {
+ return true;
+ }
+
+ public void setValue(Value value) throws ClassNotLoadedException, InvalidTypeException {
+ myEvaluatedArrayReference.setValue(myEvaluatedIndex, value);
+ }
+
+ public Type getExpectedType() throws EvaluateException {
+ try {
+ ArrayType type = (ArrayType)myEvaluatedArrayReference.referenceType();
+ return type.componentType();
+ }
+ catch (ClassNotLoadedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ public NodeDescriptorImpl getInspectItem(Project project) {
+ return new ArrayElementDescriptorImpl(project, myEvaluatedArrayReference, myEvaluatedIndex);
+ }
+ };
+ }
+ return modifier;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ArrayInitializerEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ArrayInitializerEvaluator.java
new file mode 100644
index 0000000..0fe3395
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ArrayInitializerEvaluator.java
@@ -0,0 +1,51 @@
+/*
+ * 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 ArrayInitializerEvaluator
+ * created Jun 28, 2001
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+
+class ArrayInitializerEvaluator implements Evaluator{
+ private final Evaluator[] myValueEvaluators;
+
+ public ArrayInitializerEvaluator(Evaluator[] valueEvaluators) {
+ myValueEvaluators = valueEvaluators;
+ }
+
+ /**
+ * @return an array of Values
+ */
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object[] values = new Object[myValueEvaluators.length];
+ for (int idx = 0; idx < myValueEvaluators.length; idx++) {
+ Evaluator evaluator = myValueEvaluators[idx];
+ values[idx] = evaluator.evaluate(context);
+ }
+ return values;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssignmentEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssignmentEvaluator.java
new file mode 100644
index 0000000..8a2f199
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssignmentEvaluator.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.*;
+
+/**
+ * @author lex
+ */
+public class AssignmentEvaluator implements Evaluator{
+ private final Evaluator myLeftEvaluator;
+ private final Evaluator myRightEvaluator;
+
+ public AssignmentEvaluator(Evaluator leftEvaluator, Evaluator rightEvaluator) {
+ myLeftEvaluator = leftEvaluator;
+ myRightEvaluator = new DisableGC(rightEvaluator);
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object right = myRightEvaluator.evaluate(context);
+ if(right != null && !(right instanceof Value)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.not.rvalue"));
+ }
+
+ myLeftEvaluator.evaluate(context);
+ Modifier modifier = myLeftEvaluator.getModifier();
+ assign(modifier, right, context);
+ return right;
+ }
+
+ static void assign(Modifier modifier, Object right, EvaluationContextImpl context) throws EvaluateException {
+ if(modifier == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.not.lvalue"));
+ }
+ try {
+ modifier.setValue(((Value)right));
+ }
+ catch (ClassNotLoadedException e) {
+ if (!context.isAutoLoadClasses()) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ try {
+ context.getDebugProcess().loadClass(context, e.className(), context.getClassLoader());
+ }
+ catch (InvocationException e1) {
+ throw EvaluateExceptionUtil.createEvaluateException(e1);
+ }
+ catch (ClassNotLoadedException e1) {
+ throw EvaluateExceptionUtil.createEvaluateException(e1);
+ }
+ catch (IncompatibleThreadStateException e1) {
+ throw EvaluateExceptionUtil.createEvaluateException(e1);
+ }
+ catch (InvalidTypeException e1) {
+ throw EvaluateExceptionUtil.createEvaluateException(e1);
+ }
+ }
+ catch (InvalidTypeException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ public Modifier getModifier() {
+ return myLeftEvaluator.getModifier();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java
new file mode 100644
index 0000000..db00e47
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java
@@ -0,0 +1,443 @@
+/*
+ * 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 BinaryExpressionEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.tree.IElementType;
+import com.sun.jdi.*;
+
+class BinaryExpressionEvaluator implements Evaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.BinaryExpressionEvaluator");
+ private final Evaluator myLeftOperand;
+ private final Evaluator myRightOperand;
+ private final IElementType myOpType;
+ private final String myExpectedType; // a result of PsiType.getCanonicalText()
+
+ public BinaryExpressionEvaluator(Evaluator leftOperand, Evaluator rightOperand, IElementType opType, String expectedType) {
+ myLeftOperand = new DisableGC(leftOperand);
+ myRightOperand = new DisableGC(rightOperand);
+ myOpType = opType;
+ myExpectedType = expectedType;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Value leftResult = (Value)myLeftOperand.evaluate(context);
+ return evaluateOperation(leftResult, myOpType, myRightOperand, myExpectedType, context);
+
+ }
+
+ static Object evaluateOperation(final Value leftResult,
+ final IElementType opType,
+ final Evaluator rightOperand,
+ final String expectedType,
+ final EvaluationContextImpl context) throws EvaluateException {
+ VirtualMachineProxyImpl vm = context.getDebugProcess().getVirtualMachineProxy();
+ if (leftResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ if (opType == JavaTokenType.OROR && v1) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, true);
+ }
+ if (opType == JavaTokenType.ANDAND && !v1) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, false);
+ }
+ }
+ Value rightResult = (Value)rightOperand.evaluate(context);
+ if (opType == JavaTokenType.PLUS) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 + v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ final double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ final double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 + v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 + v2);
+ }
+ if (leftResult instanceof StringReference || rightResult instanceof StringReference) {
+ String v1 = DebuggerUtilsEx.getValueAsString(context, leftResult);
+ String v2 = DebuggerUtilsEx.getValueAsString(context, rightResult);
+ return vm.mirrorOf(v1 + v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "+"));
+ }
+ else if (opType == JavaTokenType.MINUS) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 - v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 - v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 - v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "-"));
+ }
+ else if (opType == JavaTokenType.ASTERISK) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 * v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 * v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 * v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "*"));
+ }
+ else if (opType == JavaTokenType.DIV) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ long v1 = ((PrimitiveValue)leftResult).longValue();
+ long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 / v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 / v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 / v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "/"));
+ }
+ else if (opType == JavaTokenType.PERC) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ long v1 = ((PrimitiveValue)leftResult).longValue();
+ long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 % v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 % v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 % v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "%"));
+ }
+ else if (opType == JavaTokenType.LTLT) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ if (leftResult instanceof ByteValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((ByteValue)leftResult).byteValue() << v2);
+ }
+ else if (leftResult instanceof ShortValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((ShortValue)leftResult).shortValue() << v2);
+ }
+ else if (leftResult instanceof IntegerValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((IntegerValue)leftResult).intValue() << v2);
+ }
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((PrimitiveValue)leftResult).longValue() << v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((CharValue)leftResult).charValue() << ((CharValue)rightResult).charValue());
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "<<"));
+ }
+ else if (opType == JavaTokenType.GTGT) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ if (leftResult instanceof ByteValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((ByteValue)leftResult).byteValue() >> v2);
+ }
+ else if (leftResult instanceof ShortValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((ShortValue)leftResult).shortValue() >> v2);
+ }
+ else if (leftResult instanceof IntegerValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((IntegerValue)leftResult).intValue() >> v2);
+ }
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((PrimitiveValue)leftResult).longValue() >> v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((CharValue)leftResult).charValue() >> ((CharValue)rightResult).charValue());
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">>"));
+ }
+ else if (opType == JavaTokenType.GTGTGT) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ if (leftResult instanceof ByteValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((ByteValue)leftResult).byteValue() >>> v2);
+ }
+ else if (leftResult instanceof ShortValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((ShortValue)leftResult).shortValue() >>> v2);
+ }
+ else if (leftResult instanceof IntegerValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((IntegerValue)leftResult).intValue() >>> v2);
+ }
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((PrimitiveValue)leftResult).longValue() >>> v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, ((CharValue)leftResult).charValue() >>> ((CharValue)rightResult).charValue());
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">>>"));
+ }
+ else if (opType == JavaTokenType.AND) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ long v1 = ((PrimitiveValue)leftResult).longValue();
+ long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 & v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 & v2);
+ }
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 & v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "&"));
+ }
+ else if (opType == JavaTokenType.OR) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ long v1 = ((PrimitiveValue)leftResult).longValue();
+ long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 | v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 | v2);
+ }
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 | v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "|"));
+ }
+ else if (opType == JavaTokenType.XOR) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ long v1 = ((PrimitiveValue)leftResult).longValue();
+ long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 ^ v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 ^ v2);
+ }
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 ^ v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "^"));
+ }
+ else if (opType == JavaTokenType.EQEQ) {
+ if (leftResult == null && rightResult == null) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, true);
+ }
+ if (leftResult == null) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, rightResult.equals(leftResult));
+ }
+ if (rightResult == null) {
+ return DebuggerUtilsEx.createValue(vm, expectedType, leftResult.equals(rightResult));
+ }
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 == v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 == v2);
+ }
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 == v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 == v2);
+ }
+ if (leftResult instanceof ObjectReference && rightResult instanceof ObjectReference) {
+ ObjectReference v1 = (ObjectReference)leftResult;
+ ObjectReference v2 = (ObjectReference)rightResult;
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1.uniqueID() == v2.uniqueID());
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "=="));
+ }
+ else if (opType == JavaTokenType.OROR) {
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 || v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "||"));
+ }
+ else if (opType == JavaTokenType.ANDAND) {
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 && v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "&&"));
+ }
+ else if (opType == JavaTokenType.NE) {
+ if (leftResult == null && rightResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, false);
+ if (leftResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, !rightResult.equals(leftResult));
+ if (rightResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, !leftResult.equals(rightResult));
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 != v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 != v2);
+ }
+ if (leftResult instanceof BooleanValue && rightResult instanceof BooleanValue) {
+ boolean v1 = ((PrimitiveValue)leftResult).booleanValue();
+ boolean v2 = ((PrimitiveValue)rightResult).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 != v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 != v2);
+ }
+ if (leftResult instanceof ObjectReference && rightResult instanceof ObjectReference) {
+ ObjectReference v1 = (ObjectReference)leftResult;
+ ObjectReference v2 = (ObjectReference)rightResult;
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1.uniqueID() != v2.uniqueID());
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "!="));
+ }
+ else if (opType == JavaTokenType.LT) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 < v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 < v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 < v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "<"));
+ }
+ else if (opType == JavaTokenType.GT) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 > v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 > v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 > v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">"));
+ }
+ else if (opType == JavaTokenType.LE) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 <= v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 <= v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 <= v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "<="));
+ }
+ else if (opType == JavaTokenType.GE) {
+ if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) {
+ final long v1 = ((PrimitiveValue)leftResult).longValue();
+ final long v2 = ((PrimitiveValue)rightResult).longValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 >= v2);
+ }
+ if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) {
+ double v1 = ((PrimitiveValue)leftResult).doubleValue();
+ double v2 = ((PrimitiveValue)rightResult).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 >= v2);
+ }
+ if (leftResult instanceof CharValue && rightResult instanceof CharValue) {
+ char v1 = ((CharValue)leftResult).charValue();
+ char v2 = ((CharValue)rightResult).charValue();
+ return DebuggerUtilsEx.createValue(vm, expectedType, v1 >= v2);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">="));
+ }
+
+ LOG.assertTrue(false);
+
+ return null;
+ }
+
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BlockStatementEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BlockStatementEvaluator.java
new file mode 100644
index 0000000..464aa6e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BlockStatementEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+
+/**
+ * @author lex
+ */
+public class BlockStatementEvaluator implements Evaluator {
+ protected Evaluator[] myStatements;
+
+ public BlockStatementEvaluator(Evaluator[] statements) {
+ myStatements = statements;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object result = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf();
+ for (Evaluator statement : myStatements) {
+ result = statement.evaluate(context);
+ }
+ return result;
+ }
+
+ public Modifier getModifier() {
+ return myStatements.length > 0 ? myStatements[myStatements.length - 1].getModifier() : null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java
new file mode 100644
index 0000000..2d757a3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2010 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 8, 2010
+ */
+public class BoxingEvaluator implements Evaluator{
+ private final Evaluator myOperand;
+
+ public BoxingEvaluator(Evaluator operand) {
+ myOperand = new DisableGC(operand);
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ final Object result = myOperand.evaluate(context);
+ if (result == null || result instanceof ObjectReference) {
+ return result;
+ }
+
+ if (result instanceof BooleanValue) {
+ return convertToWrapper(context, (BooleanValue)result, "java.lang.Boolean");
+ }
+ if (result instanceof ByteValue) {
+ return convertToWrapper(context, (ByteValue)result, "java.lang.Byte");
+ }
+ if (result instanceof CharValue) {
+ return convertToWrapper(context, (CharValue)result, "java.lang.Character");
+ }
+ if (result instanceof ShortValue) {
+ return convertToWrapper(context, (ShortValue)result, "java.lang.Short");
+ }
+ if (result instanceof IntegerValue) {
+ return convertToWrapper(context, (IntegerValue)result, "java.lang.Integer");
+ }
+ if (result instanceof LongValue) {
+ return convertToWrapper(context, (LongValue)result, "java.lang.Long");
+ }
+ if (result instanceof FloatValue) {
+ return convertToWrapper(context, (FloatValue)result, "java.lang.Float");
+ }
+ if (result instanceof DoubleValue) {
+ return convertToWrapper(context, (DoubleValue)result, "java.lang.Double");
+ }
+ throw new EvaluateException("Cannot perform boxing conversion for a value of type " + ((Value)result).type().name());
+ }
+
+ @Nullable
+ public Modifier getModifier() {
+ return null;
+ }
+
+ private static Value convertToWrapper(EvaluationContextImpl context, PrimitiveValue value, String wrapperTypeName) throws
+ EvaluateException {
+ final DebugProcessImpl process = context.getDebugProcess();
+ final ClassType wrapperClass = (ClassType)process.findClass(context, wrapperTypeName, null);
+ final String methodSignature = "(" + JVMNameUtil.getPrimitiveSignature(value.type().name()) + ")L" + wrapperTypeName.replace('.', '/') + ";";
+
+ List<Method> methods = wrapperClass.methodsByName("valueOf", methodSignature);
+ if (methods.size() == 0) { // older JDK version
+ methods = wrapperClass.methodsByName("<init>", methodSignature);
+ }
+ if (methods.size() == 0) {
+ throw new EvaluateException("Cannot construct wrapper object for value of type " + value.type() + ": Unable to find either valueOf() or constructor method");
+ }
+
+ final Method factoryMethod = methods.get(0);
+
+ final ArrayList args = new ArrayList();
+ args.add(value);
+
+ return process.invokeMethod(context, wrapperClass, factoryMethod, args);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BreakContinueStatementEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BreakContinueStatementEvaluator.java
new file mode 100644
index 0000000..e65a322
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BreakContinueStatementEvaluator.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+
+/**
+ * @author lex
+ */
+public class BreakContinueStatementEvaluator {
+ private BreakContinueStatementEvaluator() {
+ }
+
+ public static Evaluator createBreakEvaluator(final String labelName) {
+ return new Evaluator() {
+ public Object evaluate(EvaluationContextImpl context) throws BreakException {
+ throw new BreakException(labelName);
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+ };
+ }
+
+ public static Evaluator createContinueEvaluator(final String labelName) {
+ return new Evaluator() {
+ public Object evaluate(EvaluationContextImpl context) throws ContinueException {
+ throw new ContinueException(labelName);
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+ };
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BreakException.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BreakException.java
new file mode 100644
index 0000000..24a7fe5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BreakException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+
+/**
+ * @author lex
+ */
+public class BreakException extends EvaluateException{
+ private final String myLabelName;
+
+ public BreakException(String labelName) {
+ super(DebuggerBundle.message("evaluation.error.lebeled.loops.not.found", labelName), null);
+ myLabelName = labelName;
+ }
+
+ public String getLabelName() {
+ return myLabelName;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ClassObjectEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ClassObjectEvaluator.java
new file mode 100644
index 0000000..d9ad11f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ClassObjectEvaluator.java
@@ -0,0 +1,47 @@
+/*
+ * 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 ClassObjectEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.DebuggerBundle;
+import com.sun.jdi.ReferenceType;
+
+public class ClassObjectEvaluator implements Evaluator {
+ private final TypeEvaluator myTypeEvaluator;
+
+ public ClassObjectEvaluator(TypeEvaluator typeEvaluator) {
+ myTypeEvaluator = typeEvaluator;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object object = myTypeEvaluator.evaluate(context);
+ if (!(object instanceof ReferenceType)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.reference.type.expected"));
+ }
+ return ((ReferenceType)object).classObject();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java
new file mode 100644
index 0000000..6572d44
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.HashMap;
+import com.sun.jdi.Value;
+
+import java.util.Map;
+
+/**
+ * @author lex
+ */
+public class CodeFragmentEvaluator extends BlockStatementEvaluator{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.CodeFragmentEvaluator");
+
+ private final CodeFragmentEvaluator myParentFragmentEvaluator;
+ private final Map<String, Object> mySyntheticLocals = new HashMap<String, Object>();
+
+ public CodeFragmentEvaluator(CodeFragmentEvaluator parentFragmentEvaluator) {
+ super(null);
+ myParentFragmentEvaluator = parentFragmentEvaluator;
+ }
+
+ public void setStatements(Evaluator[] evaluators) {
+ myStatements = evaluators;
+ }
+
+ public Value getValue(String localName, VirtualMachineProxyImpl vm) throws EvaluateException {
+ if(!mySyntheticLocals.containsKey(localName)) {
+ if(myParentFragmentEvaluator != null){
+ return myParentFragmentEvaluator.getValue(localName, vm);
+ } else {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.variable.not.declared", localName));
+ }
+ }
+ Object value = mySyntheticLocals.get(localName);
+ if(value instanceof Value) {
+ return (Value)value;
+ }
+ else if(value == null) {
+ return null;
+ }
+ else if(value instanceof Boolean) {
+ return vm.mirrorOf(((Boolean)value).booleanValue());
+ }
+ else if(value instanceof Byte) {
+ return vm.mirrorOf(((Byte)value).byteValue());
+ }
+ else if(value instanceof Character) {
+ return vm.mirrorOf(((Character)value).charValue());
+ }
+ else if(value instanceof Short) {
+ return vm.mirrorOf(((Short)value).shortValue());
+ }
+ else if(value instanceof Integer) {
+ return vm.mirrorOf(((Integer)value).intValue());
+ }
+ else if(value instanceof Long) {
+ return vm.mirrorOf(((Long)value).longValue());
+ }
+ else if(value instanceof Float) {
+ return vm.mirrorOf(((Float)value).floatValue());
+ }
+ else if(value instanceof Double) {
+ return vm.mirrorOf(((Double)value).doubleValue());
+ }
+ else if(value instanceof String) {
+ return vm.mirrorOf((String)value);
+ }
+ else {
+ LOG.error("unknown default initializer type " + value.getClass().getName());
+ return null;
+ }
+ }
+
+ private boolean hasValue(String localName) {
+ if(!mySyntheticLocals.containsKey(localName)) {
+ if(myParentFragmentEvaluator != null){
+ return myParentFragmentEvaluator.hasValue(localName);
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public void setInitialValue(String localName, Object value) throws EvaluateException {
+ LOG.assertTrue(!(value instanceof Value), "use setValue for jdi values");
+ if(hasValue(localName)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.variable.already.declared", localName));
+ }
+ mySyntheticLocals.put(localName, value);
+ }
+
+ public void setValue(String localName, Value value) throws EvaluateException {
+ if(!mySyntheticLocals.containsKey(localName)) {
+ if(myParentFragmentEvaluator != null){
+ myParentFragmentEvaluator.setValue(localName, value);
+ } else {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.variable.not.declared", localName));
+ }
+ }
+ mySyntheticLocals.put(localName, value);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ConditionalExpressionEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ConditionalExpressionEvaluator.java
new file mode 100644
index 0000000..3514d35
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ConditionalExpressionEvaluator.java
@@ -0,0 +1,52 @@
+/*
+ * 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 ConditionalExpressionEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.DebuggerBundle;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.Value;
+
+class ConditionalExpressionEvaluator implements Evaluator {
+ private final Evaluator myConditionEvaluator;
+ private final Evaluator myThenEvaluator;
+ private final Evaluator myElseEvaluator;
+
+ public ConditionalExpressionEvaluator(Evaluator conditionEvaluator, Evaluator thenEvaluator, Evaluator elseEvaluator) {
+ myConditionEvaluator = conditionEvaluator;
+ myThenEvaluator = thenEvaluator;
+ myElseEvaluator = elseEvaluator;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Value condition = (Value)myConditionEvaluator.evaluate(context);
+ if (condition == null || !(condition instanceof BooleanValue)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.boolean.condition.expected"));
+ }
+ return ((BooleanValue)condition).booleanValue()? myThenEvaluator.evaluate(context) : myElseEvaluator.evaluate(context);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ContinueException.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ContinueException.java
new file mode 100644
index 0000000..fd0dff3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ContinueException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+
+/**
+ * @authorlex
+ */
+public class ContinueException extends EvaluateException{
+ private final String myLabelName;
+
+ public ContinueException(String labelName) {
+ super(DebuggerBundle.message("evaluation.error.lebeled.loops.not.found", labelName), null);
+ myLabelName = labelName;
+ }
+
+ public String getLabelName() {
+ return myLabelName;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/DisableGC.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/DisableGC.java
new file mode 100644
index 0000000..1c5e718
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/DisableGC.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2010 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.ObjectReference;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: 9/30/10
+ */
+public class DisableGC implements Evaluator{
+ private final Evaluator myDelegate;
+
+ public DisableGC(Evaluator delegate) {
+ myDelegate = delegate;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ final Object result = myDelegate.evaluate(context);
+ if (result instanceof ObjectReference) {
+ context.getSuspendContext().keep((ObjectReference)result);
+ }
+ return result;
+ }
+
+ public Evaluator getDelegate() {
+ return myDelegate;
+ }
+
+ public Modifier getModifier() {
+ return myDelegate.getModifier();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/Evaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/Evaluator.java
new file mode 100644
index 0000000..9c2efc4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/Evaluator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/*
+ * Interface Evaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+
+public interface Evaluator {
+ /**
+ * @throws com.intellij.debugger.engine.evaluation.EvaluateException
+ */
+ Object evaluate(EvaluationContextImpl context) throws EvaluateException;
+
+ /**
+ * In order to obtain a modifier the expression must be evaluated first
+ * @return a modifier object allowing to set a value in case the expression is lvalue,
+ * otherwise null is returned
+ */
+ Modifier getModifier();
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java
new file mode 100644
index 0000000..a596e30
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java
@@ -0,0 +1,1254 @@
+/*
+ * Copyright 2000-2012 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 EvaluatorBuilderImpl
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.codeInsight.daemon.JavaErrorMessages;
+import com.intellij.codeInsight.daemon.impl.HighlightInfo;
+import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.JVMName;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.ui.DebuggerEditorImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.PsiTypesUtil;
+import com.intellij.psi.util.TypeConversionUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class EvaluatorBuilderImpl implements EvaluatorBuilder {
+ private static final EvaluatorBuilderImpl ourInstance = new EvaluatorBuilderImpl();
+
+ private EvaluatorBuilderImpl() {
+ }
+
+ public static EvaluatorBuilder getInstance() {
+ return ourInstance;
+ }
+
+ public static ExpressionEvaluator build(final TextWithImports text, final PsiElement contextElement, final SourcePosition position) throws EvaluateException {
+ if (contextElement == null) {
+ throw EvaluateExceptionUtil.CANNOT_FIND_SOURCE_CLASS;
+ }
+
+ final Project project = contextElement.getProject();
+
+ CodeFragmentFactory factory = DebuggerEditorImpl.findAppropriateFactory(text, contextElement);
+ PsiCodeFragment codeFragment = new CodeFragmentFactoryContextWrapper(factory).createCodeFragment(text, contextElement, project);
+ if(codeFragment == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", text.getText()));
+ }
+ codeFragment.forceResolveScope(GlobalSearchScope.allScope(project));
+ DebuggerUtils.checkSyntax(codeFragment);
+
+ EvaluatorBuilder evaluatorBuilder = factory.getEvaluatorBuilder();
+
+ return evaluatorBuilder.build(codeFragment, position);
+ }
+
+ public ExpressionEvaluator build(final PsiElement codeFragment, final SourcePosition position) throws EvaluateException {
+ return new Builder(position).buildElement(codeFragment);
+ }
+
+ private static class Builder extends JavaElementVisitor {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl");
+ private Evaluator myResult = null;
+ private PsiClass myContextPsiClass;
+ private CodeFragmentEvaluator myCurrentFragmentEvaluator;
+ private final Set<JavaCodeFragment> myVisitedFragments = new HashSet<JavaCodeFragment>();
+ @Nullable
+ private final SourcePosition myPosition;
+
+ private Builder(@Nullable SourcePosition position) {
+ myPosition = position;
+ }
+
+ @Override
+ public void visitCodeFragment(JavaCodeFragment codeFragment) {
+ myVisitedFragments.add(codeFragment);
+ ArrayList<Evaluator> evaluators = new ArrayList<Evaluator>();
+
+ CodeFragmentEvaluator oldFragmentEvaluator = myCurrentFragmentEvaluator;
+ myCurrentFragmentEvaluator = new CodeFragmentEvaluator(oldFragmentEvaluator);
+
+ for (PsiElement child = codeFragment.getFirstChild(); child != null; child = child.getNextSibling()) {
+ child.accept(this);
+ if(myResult != null) {
+ evaluators.add(myResult);
+ }
+ myResult = null;
+ }
+
+ myCurrentFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()]));
+ myResult = myCurrentFragmentEvaluator;
+
+ myCurrentFragmentEvaluator = oldFragmentEvaluator;
+ }
+
+ @Override
+ public void visitErrorElement(PsiErrorElement element) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.getText()));
+ }
+
+ @Override
+ public void visitAssignmentExpression(PsiAssignmentExpression expression) {
+ final PsiExpression rExpression = expression.getRExpression();
+ if(rExpression == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
+ }
+
+ rExpression.accept(this);
+ Evaluator rEvaluator = myResult;
+
+ if(expression.getOperationTokenType() != JavaTokenType.EQ) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.operation.not.supported", expression.getOperationSign().getText()));
+ }
+
+ final PsiExpression lExpression = expression.getLExpression();
+
+ final PsiType lType = lExpression.getType();
+ if(lType == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", lExpression.getText()));
+ }
+
+ if(!TypeConversionUtil.areTypesAssignmentCompatible(lType, rExpression) && rExpression.getType() != null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", expression.getOperationSign().getText()));
+ }
+ lExpression.accept(this);
+ Evaluator lEvaluator = myResult;
+
+ rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(lType, rExpression.getType(), rEvaluator);
+
+ myResult = new AssignmentEvaluator(lEvaluator, rEvaluator);
+ }
+
+ // returns rEvaluator possibly wrapped with boxing/unboxing and casting evaluators
+ private static Evaluator handleAssignmentBoxingAndPrimitiveTypeConversions(PsiType lType, PsiType rType, Evaluator rEvaluator) {
+ final PsiType unboxedLType = PsiPrimitiveType.getUnboxedType(lType);
+
+ if (unboxedLType != null) {
+ if (rType instanceof PsiPrimitiveType && !PsiType.NULL.equals(rType)) {
+ if (!rType.equals(unboxedLType)) {
+ rEvaluator = new TypeCastEvaluator(rEvaluator, unboxedLType.getCanonicalText(), true);
+ }
+ rEvaluator = new BoxingEvaluator(rEvaluator);
+ }
+ }
+ else {
+ // either primitive type or not unboxable type
+ if (lType instanceof PsiPrimitiveType) {
+ if (rType instanceof PsiClassType) {
+ rEvaluator = new UnBoxingEvaluator(rEvaluator);
+ }
+ final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType);
+ final PsiType _rType = unboxedRType != null? unboxedRType : rType;
+ if (_rType instanceof PsiPrimitiveType && !PsiType.NULL.equals(_rType)) {
+ if (!lType.equals(_rType)) {
+ rEvaluator = new TypeCastEvaluator(rEvaluator, lType.getCanonicalText(), true);
+ }
+ }
+ }
+ }
+ return rEvaluator;
+ }
+
+ @Override
+ public void visitStatement(PsiStatement statement) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.statement.not.supported", statement.getText()));
+ }
+
+ @Override
+ public void visitBlockStatement(PsiBlockStatement statement) {
+ PsiStatement[] statements = statement.getCodeBlock().getStatements();
+ Evaluator [] evaluators = new Evaluator[statements.length];
+ for (int i = 0; i < statements.length; i++) {
+ PsiStatement psiStatement = statements[i];
+ psiStatement.accept(this);
+ evaluators[i] = new DisableGC(myResult);
+ myResult = null;
+ }
+ myResult = new BlockStatementEvaluator(evaluators);
+ }
+
+ @Override
+ public void visitWhileStatement(PsiWhileStatement statement) {
+ PsiStatement body = statement.getBody();
+ if(body == null) return;
+ body.accept(this);
+ Evaluator bodyEvaluator = myResult;
+
+ PsiExpression condition = statement.getCondition();
+ if(condition == null) return;
+ condition.accept(this);
+ String label = null;
+ if(statement.getParent() instanceof PsiLabeledStatement) {
+ label = ((PsiLabeledStatement)statement.getParent()).getLabelIdentifier().getText();
+ }
+ myResult = new WhileStatementEvaluator(myResult, bodyEvaluator, label);
+ }
+
+ @Override
+ public void visitForStatement(PsiForStatement statement) {
+ PsiStatement initializer = statement.getInitialization();
+ Evaluator initializerEvaluator = null;
+ if(initializer != null){
+ initializer.accept(this);
+ initializerEvaluator = myResult;
+ }
+
+ PsiExpression condition = statement.getCondition();
+ Evaluator conditionEvaluator = null;
+ if(condition != null) {
+ condition.accept(this);
+ conditionEvaluator = myResult;
+ }
+
+ PsiStatement update = statement.getUpdate();
+ Evaluator updateEvaluator = null;
+ if(update != null){
+ update.accept(this);
+ updateEvaluator = myResult;
+ }
+
+ PsiStatement body = statement.getBody();
+ if(body == null) return;
+ body.accept(this);
+ Evaluator bodyEvaluator = myResult;
+
+ String label = null;
+ if(statement.getParent() instanceof PsiLabeledStatement) {
+ label = ((PsiLabeledStatement)statement.getParent()).getLabelIdentifier().getText();
+ }
+ myResult = new ForStatementEvaluator(initializerEvaluator, conditionEvaluator, updateEvaluator, bodyEvaluator, label);
+ }
+
+ @Override
+ public void visitIfStatement(PsiIfStatement statement) {
+ PsiStatement thenBranch = statement.getThenBranch();
+ if(thenBranch == null) return;
+ thenBranch.accept(this);
+ Evaluator thenEvaluator = myResult;
+
+ PsiStatement elseBranch = statement.getElseBranch();
+ Evaluator elseEvaluator = null;
+ if(elseBranch != null){
+ elseBranch.accept(this);
+ elseEvaluator = myResult;
+ }
+
+ PsiExpression condition = statement.getCondition();
+ if(condition == null) return;
+ condition.accept(this);
+
+ myResult = new IfStatementEvaluator(myResult, thenEvaluator, elseEvaluator);
+ }
+
+ @Override
+ public void visitBreakStatement(PsiBreakStatement statement) {
+ PsiIdentifier labelIdentifier = statement.getLabelIdentifier();
+ myResult = BreakContinueStatementEvaluator.createBreakEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null);
+ }
+
+ @Override
+ public void visitContinueStatement(PsiContinueStatement statement) {
+ PsiIdentifier labelIdentifier = statement.getLabelIdentifier();
+ myResult = BreakContinueStatementEvaluator.createContinueEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null);
+ }
+
+ @Override
+ public void visitExpressionStatement(PsiExpressionStatement statement) {
+ statement.getExpression().accept(this);
+ }
+
+ @Override
+ public void visitExpression(PsiExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitExpression " + expression);
+ }
+ }
+
+ @Override
+ public void visitPolyadicExpression(PsiPolyadicExpression wideExpression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitPolyadicExpression " + wideExpression);
+ }
+ PsiExpression[] operands = wideExpression.getOperands();
+ operands[0].accept(this);
+ Evaluator result = myResult;
+ PsiType lType = operands[0].getType();
+ for (int i = 1; i < operands.length; i++) {
+ PsiExpression expression = operands[i];
+ if (expression == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", wideExpression.getText()));
+ return;
+ }
+ expression.accept(this);
+ Evaluator rResult = myResult;
+ IElementType opType = wideExpression.getOperationTokenType();
+ PsiType rType = expression.getType();
+ if (rType == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
+ }
+ final PsiType typeForBinOp = TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, opType, true);
+ if (typeForBinOp == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", wideExpression.getText()));
+ }
+ myResult = createBinaryEvaluator(result, lType, rResult, rType, opType, typeForBinOp);
+ lType = typeForBinOp;
+ result = myResult;
+ }
+ }
+
+ // constructs binary evaluator handling unboxing and numeric promotion issues
+ private static BinaryExpressionEvaluator createBinaryEvaluator(Evaluator lResult,
+ PsiType lType,
+ Evaluator rResult,
+ @NotNull PsiType rType,
+ @NotNull IElementType operation,
+ @NotNull PsiType expressionExpectedType) {
+ // handle unboxing if neccesary
+ if (isUnboxingInBinaryExpressionApplicable(lType, rType, operation)) {
+ if (rType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(rType.getCanonicalText())) {
+ rResult = new UnBoxingEvaluator(rResult);
+ }
+ if (lType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(lType.getCanonicalText())) {
+ lResult = new UnBoxingEvaluator(lResult);
+ }
+ }
+ if (isBinaryNumericPromotionApplicable(lType, rType, operation)) {
+ PsiType _lType = lType;
+ final PsiPrimitiveType unboxedLType = PsiPrimitiveType.getUnboxedType(lType);
+ if (unboxedLType != null) {
+ _lType = unboxedLType;
+ }
+
+ PsiType _rType = rType;
+ final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType);
+ if (unboxedRType != null) {
+ _rType = unboxedRType;
+ }
+
+ // handle numeric promotion
+ if (PsiType.DOUBLE.equals(_lType)) {
+ if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.DOUBLE)) {
+ rResult = new TypeCastEvaluator(rResult, PsiType.DOUBLE.getCanonicalText(), true);
+ }
+ }
+ else if (PsiType.DOUBLE.equals(_rType)) {
+ if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.DOUBLE)) {
+ lResult = new TypeCastEvaluator(lResult, PsiType.DOUBLE.getCanonicalText(), true);
+ }
+ }
+ else if (PsiType.FLOAT.equals(_lType)) {
+ if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.FLOAT)) {
+ rResult = new TypeCastEvaluator(rResult, PsiType.FLOAT.getCanonicalText(), true);
+ }
+ }
+ else if (PsiType.FLOAT.equals(_rType)) {
+ if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.FLOAT)) {
+ lResult = new TypeCastEvaluator(lResult, PsiType.FLOAT.getCanonicalText(), true);
+ }
+ }
+ else if (PsiType.LONG.equals(_lType)) {
+ if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.LONG)) {
+ rResult = new TypeCastEvaluator(rResult, PsiType.LONG.getCanonicalText(), true);
+ }
+ }
+ else if (PsiType.LONG.equals(_rType)) {
+ if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.LONG)) {
+ lResult = new TypeCastEvaluator(lResult, PsiType.LONG.getCanonicalText(), true);
+ }
+ }
+ else {
+ if (!PsiType.INT.equals(_lType) && TypeConversionUtil.areTypesConvertible(_lType, PsiType.INT)) {
+ lResult = new TypeCastEvaluator(lResult, PsiType.INT.getCanonicalText(), true);
+ }
+ if (!PsiType.INT.equals(_rType) && TypeConversionUtil.areTypesConvertible(_rType, PsiType.INT)) {
+ rResult = new TypeCastEvaluator(rResult, PsiType.INT.getCanonicalText(), true);
+ }
+ }
+ }
+
+ return new BinaryExpressionEvaluator(lResult, rResult, operation, expressionExpectedType.getCanonicalText());
+ }
+
+ private static boolean isBinaryNumericPromotionApplicable(PsiType lType, PsiType rType, IElementType opType) {
+ if (lType == null || rType == null) {
+ return false;
+ }
+ if (!TypeConversionUtil.isNumericType(lType) || !TypeConversionUtil.isNumericType(rType)) {
+ return false;
+ }
+ if (opType == JavaTokenType.EQEQ || opType == JavaTokenType.NE) {
+ if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) {
+ return false;
+ }
+ if (lType instanceof PsiClassType && rType instanceof PsiClassType) {
+ return false;
+ }
+ if (lType instanceof PsiClassType) {
+ return PsiPrimitiveType.getUnboxedType(lType) != null; // should be unboxable
+ }
+ if (rType instanceof PsiClassType) {
+ return PsiPrimitiveType.getUnboxedType(rType) != null; // should be unboxable
+ }
+ return true;
+ }
+
+ return opType == JavaTokenType.ASTERISK ||
+ opType == JavaTokenType.DIV ||
+ opType == JavaTokenType.PERC ||
+ opType == JavaTokenType.PLUS ||
+ opType == JavaTokenType.MINUS ||
+ opType == JavaTokenType.LT ||
+ opType == JavaTokenType.LE ||
+ opType == JavaTokenType.GT ||
+ opType == JavaTokenType.GE ||
+ opType == JavaTokenType.AND ||
+ opType == JavaTokenType.XOR ||
+ opType == JavaTokenType.OR;
+
+ }
+
+ private static boolean isUnboxingInBinaryExpressionApplicable(PsiType lType, PsiType rType, IElementType opCode) {
+ if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) {
+ return false;
+ }
+ // handle '==' and '!=' separately
+ if (opCode == JavaTokenType.EQEQ || opCode == JavaTokenType.NE) {
+ return lType instanceof PsiPrimitiveType && rType instanceof PsiClassType ||
+ lType instanceof PsiClassType && rType instanceof PsiPrimitiveType;
+ }
+ // all other operations at least one should be of class type
+ return lType instanceof PsiClassType || rType instanceof PsiClassType;
+ }
+
+ /**
+ * @param type
+ * @return promotion type to cast to or null if no casting needed
+ */
+ @Nullable
+ private static PsiType calcUnaryNumericPromotionType(PsiPrimitiveType type) {
+ if (PsiType.BYTE.equals(type) || PsiType.SHORT.equals(type) || PsiType.CHAR.equals(type) || PsiType.INT.equals(type)) {
+ return PsiType.INT;
+ }
+ return null;
+ }
+
+ @Override
+ public void visitDeclarationStatement(PsiDeclarationStatement statement) {
+ List<Evaluator> evaluators = new ArrayList<Evaluator>();
+
+ PsiElement[] declaredElements = statement.getDeclaredElements();
+ for (PsiElement declaredElement : declaredElements) {
+ if (declaredElement instanceof PsiLocalVariable) {
+ if (myCurrentFragmentEvaluator != null) {
+ final PsiLocalVariable localVariable = (PsiLocalVariable)declaredElement;
+
+ final PsiType lType = localVariable.getType();
+
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(localVariable.getProject()).getElementFactory();
+ try {
+ PsiExpression initialValue = elementFactory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(lType), null);
+ Object value = JavaConstantExpressionEvaluator.computeConstantExpression(initialValue, true);
+ myCurrentFragmentEvaluator.setInitialValue(localVariable.getName(), value);
+ }
+ catch (IncorrectOperationException e) {
+ LOG.error(e);
+ }
+ catch (EvaluateException e) {
+ throw new EvaluateRuntimeException(e);
+ }
+
+ PsiExpression initializer = localVariable.getInitializer();
+ if (initializer != null) {
+ try {
+ if (!TypeConversionUtil.areTypesAssignmentCompatible(lType, initializer)) {
+ throwEvaluateException(
+ DebuggerBundle.message("evaluation.error.incompatible.variable.initializer.type", localVariable.getName()));
+ }
+ final PsiType rType = initializer.getType();
+ initializer.accept(this);
+ Evaluator rEvaluator = myResult;
+
+ PsiExpression localVarReference = elementFactory.createExpressionFromText(localVariable.getName(), initializer);
+
+ localVarReference.accept(this);
+ Evaluator lEvaluator = myResult;
+ rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(localVarReference.getType(), rType, rEvaluator);
+
+ Evaluator assignment = new AssignmentEvaluator(lEvaluator, rEvaluator);
+ evaluators.add(assignment);
+ }
+ catch (IncorrectOperationException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ else {
+ throw new EvaluateRuntimeException(new EvaluateException(
+ DebuggerBundle.message("evaluation.error.local.variable.declarations.not.supported"), null));
+ }
+ }
+ else {
+ throw new EvaluateRuntimeException(new EvaluateException(
+ DebuggerBundle.message("evaluation.error.unsupported.declaration", declaredElement.getText()), null));
+ }
+ }
+
+ if(!evaluators.isEmpty()) {
+ CodeFragmentEvaluator codeFragmentEvaluator = new CodeFragmentEvaluator(myCurrentFragmentEvaluator);
+ codeFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()]));
+ myResult = codeFragmentEvaluator;
+ } else {
+ myResult = null;
+ }
+ }
+
+ @Override
+ public void visitConditionalExpression(PsiConditionalExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitConditionalExpression " + expression);
+ }
+ final PsiExpression thenExpression = expression.getThenExpression();
+ final PsiExpression elseExpression = expression.getElseExpression();
+ if (thenExpression == null || elseExpression == null){
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
+ }
+ PsiExpression condition = expression.getCondition();
+ condition.accept(this);
+ if (myResult == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", condition.getText())); return;
+ }
+ Evaluator conditionEvaluator = myResult;
+ thenExpression.accept(this);
+ if (myResult == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", thenExpression.getText())); return;
+ }
+ Evaluator thenEvaluator = myResult;
+ elseExpression.accept(this);
+ if (myResult == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", elseExpression.getText())); return;
+ }
+ Evaluator elseEvaluator = myResult;
+ myResult = new ConditionalExpressionEvaluator(conditionEvaluator, thenEvaluator, elseEvaluator);
+ }
+
+ @Override
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitReferenceExpression " + expression);
+ }
+ PsiExpression qualifier = expression.getQualifierExpression();
+ JavaResolveResult resolveResult = expression.advancedResolve(true);
+ PsiElement element = resolveResult.getElement();
+
+ if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
+ final Value labeledValue = element.getUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY);
+ if (labeledValue != null) {
+ myResult = new IdentityEvaluator(labeledValue);
+ return;
+ }
+ //synthetic variable
+ final PsiFile containingFile = element.getContainingFile();
+ if(containingFile instanceof PsiCodeFragment && myCurrentFragmentEvaluator != null && myVisitedFragments.contains(containingFile)) {
+ // psiVariable may live in PsiCodeFragment not only in debugger editors, for example Fabrique has such variables.
+ // So treat it as synthetic var only when this code fragment is located in DebuggerEditor,
+ // that's why we need to check that containing code fragment is the one we visited
+ myResult = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, ((PsiVariable)element).getName());
+ return;
+ }
+ // local variable
+ final PsiVariable psiVar = (PsiVariable)element;
+ final String localName = psiVar.getName();
+ PsiClass variableClass = getContainingClass(psiVar);
+ if (getContextPsiClass() == null || getContextPsiClass().equals(variableClass)) {
+ final LocalVariableEvaluator localVarEvaluator = new LocalVariableEvaluator(localName, ContextUtil.isJspImplicit(element));
+ if (psiVar instanceof PsiParameter) {
+ final PsiParameter param = (PsiParameter)psiVar;
+ final PsiParameterList paramList = PsiTreeUtil.getParentOfType(param, PsiParameterList.class, true);
+ if (paramList != null) {
+ localVarEvaluator.setParameterIndex(paramList.getParameterIndex(param));
+ }
+ }
+ myResult = localVarEvaluator;
+ return;
+ }
+ // the expression references final var outside the context's class (in some of the outer classes)
+ int iterationCount = 0;
+ PsiClass aClass = getOuterClass(getContextPsiClass());
+ while (aClass != null && !aClass.equals(variableClass)) {
+ iterationCount++;
+ aClass = getOuterClass(aClass);
+ }
+ if (aClass != null) {
+ PsiExpression initializer = psiVar.getInitializer();
+ if(initializer != null) {
+ Object value = JavaPsiFacade.getInstance(psiVar.getProject()).getConstantEvaluationHelper().computeConstantExpression(initializer);
+ if(value != null) {
+ PsiType type = resolveResult.getSubstitutor().substitute(psiVar.getType());
+ myResult = new LiteralEvaluator(value, type.getCanonicalText());
+ return;
+ }
+ }
+ Evaluator objectEvaluator = new ThisEvaluator(iterationCount);
+ //noinspection HardCodedStringLiteral
+ final PsiClass classAt = myPosition != null? JVMNameUtil.getClassAt(myPosition) : null;
+ FieldEvaluator.TargetClassFilter filter = FieldEvaluator.createClassFilter(classAt != null? classAt : getContextPsiClass());
+ myResult = new FieldEvaluator(objectEvaluator, filter, "val$" + localName);
+ return;
+ }
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.local.variable.missing.from.class.closure", localName));
+ }
+ else if (element instanceof PsiField) {
+ final PsiField psiField = (PsiField)element;
+ final PsiClass fieldClass = psiField.getContainingClass();
+ if(fieldClass == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.resolve.field.class", psiField.getName())); return;
+ }
+ Evaluator objectEvaluator;
+ if (psiField.hasModifierProperty(PsiModifier.STATIC)) {
+ objectEvaluator = new TypeEvaluator(JVMNameUtil.getContextClassJVMQualifiedName(SourcePosition.createFromElement(psiField)));
+ }
+ else if(qualifier != null) {
+ qualifier.accept(this);
+ objectEvaluator = myResult;
+ }
+ else if (fieldClass.equals(getContextPsiClass()) || getContextPsiClass().isInheritor(fieldClass, true)) {
+ objectEvaluator = new ThisEvaluator();
+ }
+ else { // myContextPsiClass != fieldClass && myContextPsiClass is not a subclass of fieldClass
+ int iterationCount = 0;
+ PsiClass aClass = getContextPsiClass();
+ while (aClass != null && !(aClass.equals(fieldClass) || aClass.isInheritor(fieldClass, true))) {
+ iterationCount++;
+ aClass = getOuterClass(aClass);
+ }
+ if (aClass == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.sources.for.field.class", psiField.getName()));
+ }
+ objectEvaluator = new ThisEvaluator(iterationCount);
+ }
+ myResult = new FieldEvaluator(objectEvaluator, FieldEvaluator.createClassFilter(fieldClass), psiField.getName());
+ }
+ else {
+ //let's guess what this could be
+ PsiElement nameElement = expression.getReferenceNameElement(); // get "b" part
+ String name;
+ if (nameElement instanceof PsiIdentifier) {
+ name = nameElement.getText();
+ }
+ else {
+ //noinspection HardCodedStringLiteral
+ final String elementDisplayString = nameElement != null ? nameElement.getText() : "(null)";
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.identifier.expected", elementDisplayString));
+ return;
+ }
+
+ if(qualifier != null) {
+ final PsiElement qualifierTarget = qualifier instanceof PsiReferenceExpression
+ ? ((PsiReferenceExpression)qualifier).resolve() : null;
+ if (qualifierTarget instanceof PsiClass) {
+ // this is a call to a 'static' field
+ PsiClass psiClass = (PsiClass)qualifierTarget;
+ final JVMName typeName = JVMNameUtil.getJVMQualifiedName(psiClass);
+ myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.createClassFilter(psiClass), name);
+ }
+ else {
+ PsiType type = qualifier.getType();
+ if(type == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.qualifier.type.unknown", qualifier.getText()));
+ }
+
+ qualifier.accept(this);
+ if (myResult == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.qualifier", qualifier.getText()));
+ }
+
+ myResult = new FieldEvaluator(myResult, FieldEvaluator.createClassFilter(type), name);
+ }
+ }
+ else {
+ myResult = new LocalVariableEvaluator(name, false);
+ }
+ }
+ }
+
+ private static void throwEvaluateException(String message) throws EvaluateRuntimeException {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(message));
+ }
+
+ @Override
+ public void visitSuperExpression(PsiSuperExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitSuperExpression " + expression);
+ }
+ final int iterationCount = calcIterationCount(expression.getQualifier());
+ myResult = new SuperEvaluator(iterationCount);
+ }
+
+ @Override
+ public void visitThisExpression(PsiThisExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitThisExpression " + expression);
+ }
+ final int iterationCount = calcIterationCount(expression.getQualifier());
+ myResult = new ThisEvaluator(iterationCount);
+ }
+
+ private int calcIterationCount(final PsiJavaCodeReferenceElement qualifier) {
+ int iterationCount = 0;
+ if (qualifier != null) {
+ PsiElement targetClass = qualifier.resolve();
+ if (targetClass == null || getContextPsiClass() == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", qualifier.getText()));
+ }
+ try {
+ PsiClass aClass = getContextPsiClass();
+ while (aClass != null && !aClass.equals(targetClass)) {
+ iterationCount++;
+ aClass = getOuterClass(aClass);
+ }
+ }
+ catch (Exception e) {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(e));
+ }
+ }
+ return iterationCount;
+ }
+
+ @Override
+ public void visitInstanceOfExpression(PsiInstanceOfExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitInstanceOfExpression " + expression);
+ }
+ PsiTypeElement checkType = expression.getCheckType();
+ if(checkType == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
+ return;
+ }
+ PsiType type = checkType.getType();
+ expression.getOperand().accept(this);
+// ClassObjectEvaluator typeEvaluator = new ClassObjectEvaluator(type.getCanonicalText());
+ Evaluator operandEvaluator = myResult;
+ myResult = new InstanceofEvaluator(operandEvaluator, new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)));
+ }
+
+ @Override
+ public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitParenthesizedExpression " + expression);
+ }
+ PsiExpression expr = expression.getExpression();
+ if (expr != null){
+ expr.accept(this);
+ }
+ }
+
+ @Override
+ public void visitPostfixExpression(PsiPostfixExpression expression) {
+ if(expression.getType() == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
+ }
+
+ final PsiExpression operandExpression = expression.getOperand();
+ operandExpression.accept(this);
+
+ final Evaluator operandEvaluator = myResult;
+
+ final IElementType operation = expression.getOperationTokenType();
+ final PsiType operandType = operandExpression.getType();
+ @Nullable final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType);
+
+ Evaluator incrementImpl = createBinaryEvaluator(
+ operandEvaluator, operandType,
+ new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT,
+ operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS,
+ unboxedOperandType!= null? unboxedOperandType : operandType
+ );
+ if (unboxedOperandType != null) {
+ incrementImpl = new BoxingEvaluator(incrementImpl);
+ }
+ myResult = new PostfixOperationEvaluator(operandEvaluator, incrementImpl);
+ }
+
+ @Override
+ public void visitPrefixExpression(final PsiPrefixExpression expression) {
+ final PsiType expressionType = expression.getType();
+ if(expressionType == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
+ }
+
+ final PsiExpression operandExpression = expression.getOperand();
+ if (operandExpression == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.operand", expression.getText()));
+ }
+
+ operandExpression.accept(this);
+ Evaluator operandEvaluator = myResult;
+
+ // handle unboxing issues
+ final PsiType operandType = operandExpression.getType();
+ @Nullable
+ final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType);
+
+ final IElementType operation = expression.getOperationTokenType();
+
+ if(operation == JavaTokenType.PLUSPLUS || operation == JavaTokenType.MINUSMINUS) {
+ try {
+ final BinaryExpressionEvaluator rightEval = createBinaryEvaluator(
+ operandEvaluator, operandType,
+ new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT,
+ operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS,
+ unboxedOperandType!= null? unboxedOperandType : operandType
+ );
+ myResult = new AssignmentEvaluator(operandEvaluator, unboxedOperandType != null? new BoxingEvaluator(rightEval) : rightEval);
+ }
+ catch (IncorrectOperationException e) {
+ LOG.error(e);
+ }
+ }
+ else {
+ if (JavaTokenType.PLUS.equals(operation) || JavaTokenType.MINUS.equals(operation)|| JavaTokenType.TILDE.equals(operation)) {
+ operandEvaluator = handleUnaryNumericPromotion(operandType, operandEvaluator);
+ }
+ else {
+ if (unboxedOperandType != null) {
+ operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
+ }
+ }
+ myResult = new UnaryExpressionEvaluator(operation, expressionType.getCanonicalText(), operandEvaluator, expression.getOperationSign().getText());
+ }
+ }
+
+ @Override
+ public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("visitMethodCallExpression " + expression);
+ }
+ final PsiExpressionList argumentList = expression.getArgumentList();
+ final PsiExpression[] argExpressions = argumentList.getExpressions();
+ List<Evaluator> argumentEvaluators = new ArrayList<Evaluator>(argExpressions.length);
+ // evaluate arguments
+ for (PsiExpression psiExpression : argExpressions) {
+ psiExpression.accept(this);
+ if (myResult == null) {
+ // cannot build evaluator
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", psiExpression.getText()));
+ }
+ argumentEvaluators.add(new DisableGC(myResult));
+ }
+ PsiReferenceExpression methodExpr = expression.getMethodExpression();
+
+ final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
+ final PsiMethod psiMethod = (PsiMethod)resolveResult.getElement();
+
+ PsiExpression qualifier = methodExpr.getQualifierExpression();
+ Evaluator objectEvaluator;
+ JVMName contextClass = null;
+
+ if(psiMethod != null) {
+ PsiClass methodPsiClass = psiMethod.getContainingClass();
+ contextClass = JVMNameUtil.getJVMQualifiedName(methodPsiClass);
+ if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
+ objectEvaluator = new TypeEvaluator(contextClass);
+ }
+ else if (qualifier != null ) {
+ qualifier.accept(this);
+ objectEvaluator = myResult;
+ }
+ else {
+ int iterationCount = 0;
+ final PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope();
+ if (currentFileResolveScope instanceof PsiClass) {
+ PsiClass aClass = getContextPsiClass();
+ while(aClass != null && !aClass.equals(currentFileResolveScope)) {
+ aClass = getOuterClass(aClass);
+ iterationCount++;
+ }
+ }
+ objectEvaluator = new ThisEvaluator(iterationCount);
+ }
+ }
+ else {
+ //trying to guess
+ if (qualifier != null) {
+ PsiType type = qualifier.getType();
+
+ if (type != null) {
+ contextClass = JVMNameUtil.getJVMQualifiedName(type);
+ }
+
+ if (qualifier instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass) {
+ // this is a call to a 'static' method
+ if (contextClass == null && type == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.qualifier.type.unknown", qualifier.getText()));
+ }
+ assert contextClass != null;
+ objectEvaluator = new TypeEvaluator(contextClass);
+ }
+ else {
+ qualifier.accept(this);
+ objectEvaluator = myResult;
+ }
+ }
+ else {
+ objectEvaluator = new ThisEvaluator();
+ contextClass = JVMNameUtil.getContextClassJVMQualifiedName(myPosition);
+ if(contextClass == null && myContextPsiClass != null) {
+ contextClass = JVMNameUtil.getJVMQualifiedName(myContextPsiClass);
+ }
+ //else {
+ // throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(
+ // DebuggerBundle.message("evaluation.error.method.not.found", methodExpr.getReferenceName()))
+ // );
+ //}
+ }
+ }
+
+ if (objectEvaluator == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
+ }
+
+ if (psiMethod != null && !psiMethod.isConstructor()) {
+ if (psiMethod.getReturnType() == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.method.return.type", psiMethod.getText()));
+ }
+ }
+
+ if (psiMethod != null) {
+ // handle autoboxing
+ final PsiParameter[] declaredParams = psiMethod.getParameterList().getParameters();
+ if (declaredParams.length > 0) {
+ final int paramCount = Math.max(declaredParams.length, argExpressions.length);
+ PsiType varargType = null;
+ for (int idx = 0; idx < paramCount; idx++) {
+ if (idx >= argExpressions.length) {
+ break; // actual arguments count is less than number of declared params
+ }
+ PsiType declaredParamType;
+ if (idx < declaredParams.length) {
+ declaredParamType = resolveResult.getSubstitutor().substitute(declaredParams[idx].getType());
+ if (declaredParamType instanceof PsiEllipsisType) {
+ declaredParamType = varargType = ((PsiEllipsisType)declaredParamType).getComponentType();
+ }
+ }
+ else if (varargType != null) {
+ declaredParamType = varargType;
+ }
+ else {
+ break;
+ }
+ final PsiType actualArgType = argExpressions[idx].getType();
+ if (TypeConversionUtil.boxingConversionApplicable(declaredParamType, actualArgType)) {
+ final Evaluator argEval = argumentEvaluators.get(idx);
+ argumentEvaluators.set(idx, declaredParamType instanceof PsiPrimitiveType ? new UnBoxingEvaluator(argEval) : new BoxingEvaluator(argEval));
+ }
+ }
+ }
+ }
+
+ myResult = new MethodEvaluator(objectEvaluator, contextClass, methodExpr.getReferenceName(), psiMethod != null ? JVMNameUtil.getJVMSignature(psiMethod) : null, argumentEvaluators);
+ }
+
+ @Override
+ public void visitLiteralExpression(PsiLiteralExpression expression) {
+ final HighlightInfo parsingError = HighlightUtil.checkLiteralExpressionParsingError(expression);
+ if (parsingError != null) {
+ throwEvaluateException(parsingError.description);
+ return;
+ }
+
+ final PsiType type = expression.getType();
+ if (type == null) {
+ throwEvaluateException(expression + ": null type");
+ return;
+ }
+
+ myResult = new LiteralEvaluator(expression.getValue(), type.getCanonicalText());
+ }
+
+ @Override
+ public void visitArrayAccessExpression(PsiArrayAccessExpression expression) {
+ final PsiExpression indexExpression = expression.getIndexExpression();
+ if(indexExpression == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
+ }
+ indexExpression.accept(this);
+ final Evaluator indexEvaluator = handleUnaryNumericPromotion(indexExpression.getType(), myResult);
+
+ expression.getArrayExpression().accept(this);
+ Evaluator arrayEvaluator = myResult;
+ myResult = new ArrayAccessEvaluator(arrayEvaluator, indexEvaluator);
+ }
+
+
+ /**
+ * Handles unboxing and numeric promotion issues for
+ * - array diumention expressions
+ * - array index expression
+ * - unary +, -, and ~ operations
+ * @param operandExpressionType
+ * @param operandEvaluator @return operandEvaluator possibly 'wrapped' with neccesary unboxing and type-casting evaluators to make returning value
+ * sutable for mentioned contexts
+ */
+ private static Evaluator handleUnaryNumericPromotion(final PsiType operandExpressionType, Evaluator operandEvaluator) {
+ final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(operandExpressionType);
+ if (unboxedType != null && !PsiType.BOOLEAN.equals(unboxedType)) {
+ operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
+ }
+
+ // handle numeric promotion
+ final PsiType _unboxedIndexType = unboxedType != null? unboxedType : operandExpressionType;
+ if (_unboxedIndexType instanceof PsiPrimitiveType) {
+ final PsiType promotionType = calcUnaryNumericPromotionType((PsiPrimitiveType)_unboxedIndexType);
+ if (promotionType != null) {
+ operandEvaluator = new TypeCastEvaluator(operandEvaluator, promotionType.getCanonicalText(), true);
+ }
+ }
+ return operandEvaluator;
+ }
+
+ @SuppressWarnings({"ConstantConditions"})
+ @Override
+ public void visitTypeCastExpression(PsiTypeCastExpression expression) {
+ final PsiExpression operandExpr = expression.getOperand();
+ operandExpr.accept(this);
+ Evaluator operandEvaluator = myResult;
+ final PsiType castType = expression.getCastType().getType();
+ final PsiType operandType = operandExpr.getType();
+
+ if (castType != null && operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType)) {
+ throw new EvaluateRuntimeException(
+ new EvaluateException(JavaErrorMessages.message("inconvertible.type.cast", HighlightUtil.formatType(operandType), HighlightUtil.formatType(castType)))
+ );
+ }
+
+ final boolean shouldPerformBoxingConversion = castType != null && operandType != null && TypeConversionUtil.boxingConversionApplicable(castType, operandType);
+ final boolean castingToPrimitive = castType instanceof PsiPrimitiveType;
+ if (shouldPerformBoxingConversion && castingToPrimitive) {
+ operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
+ }
+
+ final boolean performCastToWrapperClass = shouldPerformBoxingConversion && !castingToPrimitive;
+
+ String castTypeName = castType.getCanonicalText();
+ if (performCastToWrapperClass) {
+ final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(castType);
+ if (unboxedType != null) {
+ castTypeName = unboxedType.getCanonicalText();
+ }
+ }
+
+ myResult = new TypeCastEvaluator(operandEvaluator, castTypeName, castingToPrimitive);
+
+ if (performCastToWrapperClass) {
+ myResult = new BoxingEvaluator(myResult);
+ }
+ }
+
+ @Override
+ public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
+ PsiType type = expression.getOperand().getType();
+
+ if (type instanceof PsiPrimitiveType) {
+ final JVMName typeName = JVMNameUtil.getJVMRawText(((PsiPrimitiveType)type).getBoxedTypeName());
+ myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.TargetClassFilter.ALL, "TYPE");
+ }
+ else {
+ myResult = new ClassObjectEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)));
+ }
+ }
+
+ @Override
+ public void visitNewExpression(final PsiNewExpression expression) {
+ PsiType expressionPsiType = expression.getType();
+ if (expressionPsiType instanceof PsiArrayType) {
+ Evaluator dimensionEvaluator = null;
+ PsiExpression[] dimensions = expression.getArrayDimensions();
+ if (dimensions.length == 1){
+ PsiExpression dimensionExpression = dimensions[0];
+ dimensionExpression.accept(this);
+ if (myResult != null) {
+ dimensionEvaluator = handleUnaryNumericPromotion(dimensionExpression.getType(), myResult);
+ }
+ else {
+ throwEvaluateException(
+ DebuggerBundle.message("evaluation.error.invalid.array.dimension.expression", dimensionExpression.getText()));
+ }
+ }
+ else if (dimensions.length > 1){
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.multi.dimensional.arrays.creation.not.supported"));
+ }
+
+ Evaluator initializerEvaluator = null;
+ PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
+ if (arrayInitializer != null) {
+ if (dimensionEvaluator != null) { // initializer already exists
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
+ }
+ arrayInitializer.accept(this);
+ if (myResult != null) {
+ initializerEvaluator = handleUnaryNumericPromotion(arrayInitializer.getType(), myResult);
+ }
+ else {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", arrayInitializer.getText()));
+ }
+ /*
+ PsiExpression[] initializers = arrayInitializer.getInitializers();
+ initializerEvaluators = new Evaluator[initializers.length];
+ for (int idx = 0; idx < initializers.length; idx++) {
+ PsiExpression initializer = initializers[idx];
+ initializer.accept(this);
+ if (myResult instanceof Evaluator) {
+ initializerEvaluators[idx] = myResult;
+ }
+ else {
+ throw new EvaluateException("Invalid expression for array initializer: " + initializer.getText(), true);
+ }
+ }
+ */
+ }
+ if (dimensionEvaluator == null && initializerEvaluator == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
+ }
+ myResult = new NewArrayInstanceEvaluator(
+ new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)),
+ dimensionEvaluator,
+ initializerEvaluator
+ );
+ }
+ else if (expressionPsiType instanceof PsiClassType){ // must be a class ref
+ PsiClass aClass = ((PsiClassType)expressionPsiType).resolve();
+ if(aClass instanceof PsiAnonymousClass) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported"));
+ }
+ PsiExpressionList argumentList = expression.getArgumentList();
+ if (argumentList == null) {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
+ }
+ PsiExpression[] argExpressions = argumentList.getExpressions();
+ PsiMethod constructor = expression.resolveConstructor();
+ if (constructor == null && argExpressions.length > 0) {
+ throw new EvaluateRuntimeException(new EvaluateException(
+ DebuggerBundle.message("evaluation.error.cannot.resolve.constructor", expression.getText()), null));
+ }
+ Evaluator[] argumentEvaluators = new Evaluator[argExpressions.length];
+ // evaluate arguments
+ for (int idx = 0; idx < argExpressions.length; idx++) {
+ PsiExpression argExpression = argExpressions[idx];
+ argExpression.accept(this);
+ if (myResult != null) {
+ argumentEvaluators[idx] = new DisableGC(myResult);
+ }
+ else {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", argExpression.getText()));
+ }
+ }
+ //noinspection HardCodedStringLiteral
+ JVMName signature = constructor != null ? JVMNameUtil.getJVMSignature(constructor) : JVMNameUtil.getJVMRawText("()V");
+ myResult = new NewClassInstanceEvaluator(
+ new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)),
+ signature,
+ argumentEvaluators
+ );
+ }
+ else {
+ if (expressionPsiType != null) {
+ throwEvaluateException("Unsupported expression type: " + expressionPsiType.getPresentableText());
+ }
+ else {
+ throwEvaluateException("Unknown type for expression: " + expression.getText());
+ }
+ }
+ }
+
+ @Override
+ public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
+ PsiExpression[] initializers = expression.getInitializers();
+ Evaluator[] evaluators = new Evaluator[initializers.length];
+ final PsiType type = expression.getType();
+ boolean primitive = type instanceof PsiArrayType && ((PsiArrayType)type).getComponentType() instanceof PsiPrimitiveType;
+ for (int idx = 0; idx < initializers.length; idx++) {
+ PsiExpression initializer = initializers[idx];
+ initializer.accept(this);
+ if (myResult != null) {
+ final Evaluator coerced =
+ primitive ? handleUnaryNumericPromotion(initializer.getType(), myResult) : new BoxingEvaluator(myResult);
+ evaluators[idx] = new DisableGC(coerced);
+ }
+ else {
+ throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", initializer.getText()));
+ }
+ }
+ myResult = new ArrayInitializerEvaluator(evaluators);
+ }
+
+ @Nullable
+ private static PsiClass getOuterClass(PsiClass aClass) {
+ return aClass == null ? null : PsiTreeUtil.getContextOfType(aClass, PsiClass.class, true);
+ }
+
+ private PsiClass getContainingClass(PsiVariable variable) {
+ PsiElement element = PsiTreeUtil.getParentOfType(variable.getParent(), PsiClass.class, false);
+ return element == null ? getContextPsiClass() : (PsiClass)element;
+ }
+
+ public PsiClass getContextPsiClass() {
+ return myContextPsiClass;
+ }
+
+ protected ExpressionEvaluator buildElement(final PsiElement element) throws EvaluateException {
+ LOG.assertTrue(element.isValid());
+
+ myContextPsiClass = PsiTreeUtil.getContextOfType(element, PsiClass.class, false);
+ try {
+ element.accept(this);
+ }
+ catch (EvaluateRuntimeException e) {
+ throw e.getCause();
+ }
+ if (myResult == null) {
+ throw EvaluateExceptionUtil
+ .createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.toString()));
+ }
+ return new ExpressionEvaluatorImpl(myResult);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.java
new file mode 100644
index 0000000..4b974c1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.Value;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jul 15, 2003
+ * Time: 1:44:35 PM
+ * To change this template use Options | File Templates.
+ */
+public class ExpressionEvaluatorImpl implements ExpressionEvaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator");
+ Evaluator myEvaluator;
+ Value myValue;
+
+ public ExpressionEvaluatorImpl(Evaluator evaluator) {
+ myEvaluator = evaluator;
+ }
+
+ //call evaluate before
+ public Value getValue() {
+ return myValue;
+ }
+
+ //call evaluate before
+ public Modifier getModifier() {
+ return myEvaluator.getModifier();
+ }
+
+ // EvaluationContextImpl should be at the same stackFrame as it was in the call to EvaluatorBuilderImpl.build
+ public Value evaluate(final EvaluationContext context) throws EvaluateException {
+ if (!context.getDebugProcess().isAttached()) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.vm.disconnected"));
+ }
+ try {
+ if (context.getFrameProxy() == null) {
+ throw EvaluateExceptionUtil.NULL_STACK_FRAME;
+ }
+
+ Object value = myEvaluator.evaluate((EvaluationContextImpl)context);
+
+ if(value != null && !(value instanceof Value)) {
+ throw EvaluateExceptionUtil
+ .createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", ""));
+ }
+
+ myValue = (Value) value;
+ return myValue;
+ }
+ catch (Throwable/*IncompatibleThreadStateException*/ e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ if(e instanceof EvaluateException)
+ throw ((EvaluateException)e);
+ else
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/FieldEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/FieldEvaluator.java
new file mode 100644
index 0000000..4e35754
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/FieldEvaluator.java
@@ -0,0 +1,252 @@
+/*
+ * 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 FieldEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiArrayType;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.util.PsiUtil;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+public class FieldEvaluator implements Evaluator {
+ private final Evaluator myObjectEvaluator;
+ private final TargetClassFilter myTargetClassFilter;
+ private final String myFieldName;
+ private Object myEvaluatedQualifier;
+ private Field myEvaluatedField;
+
+ public interface TargetClassFilter {
+ TargetClassFilter ALL = new TargetClassFilter() {
+ public boolean acceptClass(final ReferenceType refType) {
+ return true;
+ }
+ };
+ boolean acceptClass(ReferenceType refType);
+ }
+
+ public FieldEvaluator(Evaluator objectEvaluator, TargetClassFilter filter, @NonNls String fieldName) {
+ myObjectEvaluator = objectEvaluator;
+ myFieldName = fieldName;
+ myTargetClassFilter = filter;
+ }
+
+ public static TargetClassFilter createClassFilter(PsiType psiType) {
+ if(psiType instanceof PsiArrayType) {
+ return TargetClassFilter.ALL;
+ }
+ PsiClass psiClass = PsiUtil.resolveClassInType(psiType);
+ if (psiClass != null) {
+ return createClassFilter(psiClass);
+ }
+ return new FQNameClassFilter(psiType.getCanonicalText());
+ }
+
+ public static TargetClassFilter createClassFilter(PsiClass psiClass) {
+ if (psiClass instanceof PsiAnonymousClass) {
+ return TargetClassFilter.ALL;
+ }
+ if (PsiUtil.isLocalClass(psiClass)) {
+ return new LocalClassFilter(psiClass.getName());
+ }
+ final String name = JVMNameUtil.getNonAnonymousClassName(psiClass);
+ return name != null? new FQNameClassFilter(name) : TargetClassFilter.ALL;
+ }
+
+ @Nullable
+ private Field findField(Type t, final EvaluationContextImpl context) throws EvaluateException {
+ if(t instanceof ClassType) {
+ ClassType cls = (ClassType) t;
+ if(myTargetClassFilter.acceptClass(cls)) {
+ return cls.fieldByName(myFieldName);
+ }
+ for (final InterfaceType interfaceType : cls.interfaces()) {
+ final Field field = findField(interfaceType, context);
+ if (field != null) {
+ return field;
+ }
+ }
+ return findField(cls.superclass(), context);
+ }
+ else if(t instanceof InterfaceType) {
+ InterfaceType iface = (InterfaceType) t;
+ if(myTargetClassFilter.acceptClass(iface)) {
+ return iface.fieldByName(myFieldName);
+ }
+ for (final InterfaceType interfaceType : iface.superinterfaces()) {
+ final Field field = findField(interfaceType, context);
+ if (field != null) {
+ return field;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ myEvaluatedField = null;
+ myEvaluatedQualifier = null;
+ Object object = myObjectEvaluator.evaluate(context);
+
+ return evaluateField(object, context);
+
+ }
+
+ private Object evaluateField(Object object, EvaluationContextImpl context) throws EvaluateException {
+ if (object instanceof ReferenceType) {
+ ReferenceType refType = (ReferenceType)object;
+ Field field = findField(refType, context);
+ if (field == null || !field.isStatic()) {
+ field = refType.fieldByName(myFieldName);
+ }
+ if (field == null || !field.isStatic()) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.static.field", myFieldName));
+ }
+ myEvaluatedField = field;
+ myEvaluatedQualifier = refType;
+ return refType.getValue(field);
+ }
+
+ if (object instanceof ObjectReference) {
+ ObjectReference objRef = (ObjectReference)object;
+ ReferenceType refType = objRef.referenceType();
+ if (!(refType instanceof ClassType || refType instanceof ArrayType)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.class.or.array.expected", myFieldName));
+ }
+
+ // expressions like 'array.length' must be treated separately
+ //noinspection HardCodedStringLiteral
+ if (objRef instanceof ArrayReference && "length".equals(myFieldName)) {
+ //noinspection HardCodedStringLiteral
+ return DebuggerUtilsEx.createValue(
+ context.getDebugProcess().getVirtualMachineProxy(),
+ "int",
+ ((ArrayReference)objRef).length()
+ );
+ }
+
+ Field field = findField(refType, context);
+ if (field == null) {
+ field = refType.fieldByName(myFieldName);
+ }
+
+ if (field == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.instance.field", myFieldName));
+ }
+ myEvaluatedQualifier = field.isStatic()? (Object)refType : (Object)objRef;
+ myEvaluatedField = field;
+ return field.isStatic()? refType.getValue(field) : objRef.getValue(field);
+ }
+
+ if(object == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(new NullPointerException());
+ }
+
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.evaluating.field", myFieldName));
+ }
+
+ public Modifier getModifier() {
+ Modifier modifier = null;
+ if (myEvaluatedField != null && (myEvaluatedQualifier instanceof ClassType || myEvaluatedQualifier instanceof ObjectReference)) {
+ modifier = new Modifier() {
+ public boolean canInspect() {
+ return myEvaluatedQualifier instanceof ObjectReference;
+ }
+
+ public boolean canSetValue() {
+ return true;
+ }
+
+ public void setValue(Value value) throws ClassNotLoadedException, InvalidTypeException {
+ if (myEvaluatedQualifier instanceof ReferenceType) {
+ ClassType classType = (ClassType)myEvaluatedQualifier;
+ classType.setValue(myEvaluatedField, value);
+ }
+ else {
+ ObjectReference objRef = (ObjectReference)myEvaluatedQualifier;
+ objRef.setValue(myEvaluatedField, value);
+ }
+ }
+
+ public Type getExpectedType() throws ClassNotLoadedException {
+ return myEvaluatedField.type();
+ }
+
+ public NodeDescriptorImpl getInspectItem(Project project) {
+ if(myEvaluatedQualifier instanceof ObjectReference) {
+ return new FieldDescriptorImpl(project, (ObjectReference)myEvaluatedQualifier, myEvaluatedField);
+ } else
+ return null;
+ }
+ };
+ }
+ return modifier;
+ }
+
+ private static final class FQNameClassFilter implements TargetClassFilter {
+ private final String myQName;
+
+ private FQNameClassFilter(String qName) {
+ myQName = qName;
+ }
+
+ public boolean acceptClass(final ReferenceType refType) {
+ return refType.name().equals(myQName);
+ }
+ }
+
+ private static final class LocalClassFilter implements TargetClassFilter{
+ private final String myLocalClassShortName;
+
+ private LocalClassFilter(String localClassShortName) {
+ myLocalClassShortName = localClassShortName;
+ }
+
+ public boolean acceptClass(final ReferenceType refType) {
+ final String name = refType.name();
+ final int index = name.lastIndexOf(myLocalClassShortName);
+ if (index < 0) {
+ return false;
+ }
+ for (int idx = index - 1; idx >= 0; idx--) {
+ final char ch = name.charAt(idx);
+ if (ch == '$') {
+ return idx < (index - 1);
+ }
+ if (!Character.isDigit(ch)) {
+ return false;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ForStatementEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ForStatementEvaluator.java
new file mode 100644
index 0000000..ff35ef7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ForStatementEvaluator.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.openapi.util.Comparing;
+import com.sun.jdi.BooleanValue;
+
+/**
+ * @author lex
+ */
+public class ForStatementEvaluator implements Evaluator {
+ private final Evaluator myInitializationEvaluator;
+ private final Evaluator myConditionEvaluator;
+ private final Evaluator myUpdateEvaluator;
+ private final Evaluator myBodyEvaluator;
+
+ private Modifier myModifier;
+ private final String myLabelName;
+
+ public ForStatementEvaluator(Evaluator initializationEvaluator,
+ Evaluator conditionEvaluator,
+ Evaluator updateEvaluator,
+ Evaluator bodyEvaluator,
+ String labelName) {
+ myInitializationEvaluator = new DisableGC(initializationEvaluator);
+ myConditionEvaluator = new DisableGC(conditionEvaluator);
+ myUpdateEvaluator = new DisableGC(updateEvaluator);
+ myBodyEvaluator = new DisableGC(bodyEvaluator);
+ myLabelName = labelName;
+ }
+
+ public Modifier getModifier() {
+ return myModifier;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object value = context.getDebugProcess().getVirtualMachineProxy().mirrorOf();
+ if (myInitializationEvaluator != null) {
+ value = myInitializationEvaluator.evaluate(context);
+ myModifier = myInitializationEvaluator.getModifier();
+ }
+
+ while (true) {
+ if (myConditionEvaluator != null) {
+ value = myConditionEvaluator.evaluate(context);
+ myModifier = myConditionEvaluator.getModifier();
+ if (!(value instanceof BooleanValue)) {
+ throw EvaluateExceptionUtil.BOOLEAN_EXPECTED;
+ }
+ else {
+ if (!((BooleanValue)value).booleanValue()) {
+ break;
+ }
+ }
+ }
+
+ try {
+ myBodyEvaluator.evaluate(context);
+ }
+ catch (BreakException e) {
+ if (Comparing.equal(e.getLabelName(), myLabelName)) {
+ break;
+ }
+ else {
+ throw e;
+ }
+ }
+ catch (ContinueException e) {
+ if (Comparing.equal(e.getLabelName(), myLabelName)) {
+ //continue;
+ }
+ else {
+ throw e;
+ }
+ }
+
+ if (myUpdateEvaluator != null) {
+ value = myUpdateEvaluator.evaluate(context);
+ myModifier = myUpdateEvaluator.getModifier();
+ }
+ }
+
+ return value;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/IdentityEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/IdentityEvaluator.java
new file mode 100644
index 0000000..4ff1a85
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/IdentityEvaluator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2010 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.Value;
+
+/**
+* @author Eugene Zhuravlev
+* Date: Feb 9, 2010
+*/
+public class IdentityEvaluator implements Evaluator {
+ private final Value myValue;
+
+ public IdentityEvaluator(Value value) {
+ myValue = value;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ return myValue;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/IfStatementEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/IfStatementEvaluator.java
new file mode 100644
index 0000000..7d518a0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/IfStatementEvaluator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.BooleanValue;
+
+/**
+ * @author lex
+ */
+public class IfStatementEvaluator implements Evaluator {
+ private final Evaluator myConditionEvaluator;
+ private final Evaluator myThenEvaluator;
+ private final Evaluator myElseEvaluator;
+
+ private Modifier myModifier;
+
+ public IfStatementEvaluator(Evaluator conditionEvaluator, Evaluator thenEvaluator, Evaluator elseEvaluator) {
+ myConditionEvaluator = new DisableGC(conditionEvaluator);
+ myThenEvaluator = new DisableGC(thenEvaluator);
+ myElseEvaluator = elseEvaluator == null ? null : new DisableGC(elseEvaluator);
+ }
+
+ public Modifier getModifier() {
+ return myModifier;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object value = myConditionEvaluator.evaluate(context);
+ if(!(value instanceof BooleanValue)) {
+ throw EvaluateExceptionUtil.BOOLEAN_EXPECTED;
+ } else {
+ if(((BooleanValue)value).booleanValue()) {
+ value = myThenEvaluator.evaluate(context);
+ myModifier = myThenEvaluator.getModifier();
+ }
+ else {
+ if(myElseEvaluator != null) {
+ value = myElseEvaluator.evaluate(context);
+ myModifier = myElseEvaluator.getModifier();
+ } else {
+ value = context.getDebugProcess().getVirtualMachineProxy().mirrorOf();
+ myModifier = null;
+ }
+ }
+ }
+ return value;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectArrayItem.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectArrayItem.java
new file mode 100644
index 0000000..0e19e78
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectArrayItem.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.sun.jdi.ArrayReference;
+
+/**
+ * User: lex
+ * Date: Oct 16, 2003
+ * Time: 2:44:38 PM
+ */
+public interface InspectArrayItem extends InspectEntity{
+ ArrayReference getArray ();
+ int getItemIndex();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectEntity.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectEntity.java
new file mode 100644
index 0000000..423ca01
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectEntity.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+/**
+ * User: lex
+ * Date: Oct 15, 2003
+ * Time: 11:38:02 PM
+ */
+public interface InspectEntity {
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectField.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectField.java
new file mode 100644
index 0000000..252fba2
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectField.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.sun.jdi.Field;
+import com.sun.jdi.ObjectReference;
+
+/**
+ * User: lex
+ * Date: Oct 15, 2003
+ * Time: 11:38:25 PM
+ */
+public interface InspectField extends InspectEntity{
+ ObjectReference getObject();
+ Field getField ();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectLocal.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectLocal.java
new file mode 100644
index 0000000..79d6b83
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InspectLocal.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.sun.jdi.LocalVariable;
+
+/**
+ * User: lex
+ * Date: Oct 15, 2003
+ * Time: 11:39:04 PM
+ *
+ * todo [lex] does this interface really required?
+ */
+public interface InspectLocal extends InspectEntity{
+ StackFrameProxyImpl getStackFrame();
+ LocalVariable getLocal ();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InstanceofEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InstanceofEvaluator.java
new file mode 100644
index 0000000..2a3c010
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/InstanceofEvaluator.java
@@ -0,0 +1,76 @@
+/*
+ * 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 InstanceofEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.PsiType;
+import com.sun.jdi.*;
+
+import java.util.LinkedList;
+import java.util.List;
+
+class InstanceofEvaluator implements Evaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.InstanceofEvaluator");
+ private final Evaluator myOperandEvaluator;
+ private final TypeEvaluator myTypeEvaluator;
+
+ public InstanceofEvaluator(Evaluator operandEvaluator, TypeEvaluator typeEvaluator) {
+ myOperandEvaluator = operandEvaluator;
+ myTypeEvaluator = typeEvaluator;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Value value = (Value)myOperandEvaluator.evaluate(context);
+ if (value == null) {
+ return DebuggerUtilsEx.createValue(context.getDebugProcess().getVirtualMachineProxy(), PsiType.BOOLEAN.getPresentableText(), false);
+ }
+ if (!(value instanceof ObjectReference)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.object.reference.expected"));
+ }
+ try {
+ ReferenceType refType = (ReferenceType)myTypeEvaluator.evaluate(context);
+ ClassObjectReference classObject = refType.classObject();
+ ClassType classRefType = (ClassType)classObject.referenceType();
+ //noinspection HardCodedStringLiteral
+ Method method = classRefType.concreteMethodByName("isAssignableFrom", "(Ljava/lang/Class;)Z");
+ List args = new LinkedList();
+ args.add(((ObjectReference)value).referenceType().classObject());
+ return context.getDebugProcess().invokeMethod(context, classObject, method, args);
+ }
+ catch (Exception e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java
new file mode 100644
index 0000000..2f4b85d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java
@@ -0,0 +1,69 @@
+/*
+ * 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 LiteralEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+
+class LiteralEvaluator implements Evaluator {
+ private final Object myValue;
+ private final String myExpectedType;
+
+ public LiteralEvaluator(Object value, String expectedType) {
+ myValue = value;
+ myExpectedType = expectedType;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ if (myValue == null) {
+ return null;
+ }
+ VirtualMachineProxyImpl vm = context.getDebugProcess().getVirtualMachineProxy();
+ if (myValue instanceof Boolean) {
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, ((Boolean)myValue).booleanValue());
+ }
+ if (myValue instanceof Character) {
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, ((Character)myValue).charValue());
+ }
+ if (myValue instanceof Double) {
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, ((Number)myValue).doubleValue());
+ }
+ if (myValue instanceof Float) {
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, ((Number)myValue).floatValue());
+ }
+ if (myValue instanceof Number) {
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, ((Number)myValue).longValue());
+ }
+ if (myValue instanceof String) {
+ return vm.mirrorOf((String)myValue);
+ }
+ throw EvaluateExceptionUtil
+ .createEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", myExpectedType));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LocalVariableEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LocalVariableEvaluator.java
new file mode 100644
index 0000000..ce060c0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LocalVariableEvaluator.java
@@ -0,0 +1,150 @@
+/*
+ * 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 LocalVariableEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.impl.watch.LocalVariableDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.*;
+
+import java.util.List;
+
+class LocalVariableEvaluator implements Evaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.LocalVariableEvaluator");
+
+ private final String myLocalVariableName;
+ private EvaluationContextImpl myContext;
+ private LocalVariableProxyImpl myEvaluatedVariable;
+ private final boolean myIsJspSpecial;
+ private int myParameterIndex = -1;
+
+ public LocalVariableEvaluator(String localVariableName, boolean isJspSpecial) {
+ myLocalVariableName = localVariableName;
+ myIsJspSpecial = isJspSpecial;
+ }
+
+ public void setParameterIndex(int parameterIndex) {
+ myParameterIndex = parameterIndex;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ StackFrameProxyImpl frameProxy = context.getFrameProxy();
+ if (frameProxy == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.stackframe"));
+ }
+
+ try {
+ ThreadReferenceProxyImpl threadProxy = null;
+ int lastFrameIndex = -1;
+
+ while (true) {
+ try {
+ LocalVariableProxyImpl local = frameProxy.visibleVariableByName(myLocalVariableName);
+ if (local != null) {
+ myEvaluatedVariable = local;
+ myContext = context;
+ return frameProxy.getValue(local);
+ }
+ }
+ catch (EvaluateException e) {
+ if (!(e.getCause() instanceof AbsentInformationException)) {
+ throw e;
+ }
+ if (myParameterIndex < 0) {
+ throw e;
+ }
+ final List<Value> values = frameProxy.getArgumentValues();
+ if (values.isEmpty() || myParameterIndex >= values.size()) {
+ throw e;
+ }
+ return values.get(myParameterIndex);
+ }
+
+ if (myIsJspSpecial) {
+ if (threadProxy == null /* initialize it lazily */) {
+ threadProxy = frameProxy.threadProxy();
+ lastFrameIndex = threadProxy.frameCount() - 1;
+ }
+ final int currentFrameIndex = frameProxy.getFrameIndex();
+ if (currentFrameIndex < lastFrameIndex) {
+ frameProxy = threadProxy.frame(currentFrameIndex + 1);
+ continue;
+ }
+ }
+
+ break;
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.local.variable.missing", myLocalVariableName));
+ }
+ catch (EvaluateException e) {
+ myEvaluatedVariable = null;
+ myContext = null;
+ throw e;
+ }
+ }
+
+ public Modifier getModifier() {
+ Modifier modifier = null;
+ if (myEvaluatedVariable != null && myContext != null) {
+ modifier = new Modifier() {
+ public boolean canInspect() {
+ return true;
+ }
+
+ public boolean canSetValue() {
+ return true;
+ }
+
+ public void setValue(Value value) throws ClassNotLoadedException, InvalidTypeException {
+ StackFrameProxyImpl frameProxy = myContext.getFrameProxy();
+ try {
+ frameProxy.setValue(myEvaluatedVariable, value);
+ }
+ catch (EvaluateException e) {
+ LOG.error(e);
+ }
+ }
+
+ public Type getExpectedType() throws ClassNotLoadedException {
+ try {
+ return myEvaluatedVariable.getType();
+ } catch (EvaluateException e) {
+ LOG.error(e);
+ return null;
+ }
+ }
+
+ public NodeDescriptorImpl getInspectItem(Project project) {
+ return new LocalVariableDescriptorImpl(project, myEvaluatedVariable);
+ }
+ };
+ }
+ return modifier;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java
new file mode 100644
index 0000000..257bad2
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java
@@ -0,0 +1,147 @@
+/*
+ * 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 MethodEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JVMName;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateRuntimeException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ReferenceType;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class MethodEvaluator implements Evaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.MethodEvaluator");
+ private final JVMName myClassName;
+ private final JVMName myMethodSignature;
+ private final String myMethodName;
+ private final List myArgumentEvaluators;
+ private final Evaluator myObjectEvaluator;
+
+ public MethodEvaluator(Evaluator objectEvaluator, JVMName className, String methodName, JVMName signature, List argumentEvaluators) {
+ myObjectEvaluator = new DisableGC(objectEvaluator);
+ myClassName = className;
+ myMethodName = methodName;
+ myMethodSignature = signature;
+ myArgumentEvaluators = argumentEvaluators;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ if(!context.getDebugProcess().isAttached()) return null;
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+
+ final boolean requiresSuperObject =
+ myObjectEvaluator instanceof SuperEvaluator ||
+ (myObjectEvaluator instanceof DisableGC && ((DisableGC)myObjectEvaluator).getDelegate() instanceof SuperEvaluator);
+
+ final Object object = myObjectEvaluator.evaluate(context);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("MethodEvaluator: object = " + object);
+ }
+ if(object == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(new NullPointerException());
+ }
+ if (!(object instanceof ObjectReference || object instanceof ClassType)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.evaluating.method", myMethodName));
+ }
+ List args = new ArrayList(myArgumentEvaluators.size());
+ for (Iterator it = myArgumentEvaluators.iterator(); it.hasNext();) {
+ Evaluator evaluator = (Evaluator)it.next();
+ args.add(evaluator.evaluate(context));
+ }
+ try {
+ ReferenceType referenceType = null;
+
+ if(object instanceof ObjectReference) {
+ final ReferenceType qualifierType = ((ObjectReference)object).referenceType();
+ referenceType = debugProcess.findClass(context, qualifierType.name(), qualifierType.classLoader());
+ }
+ else if(object instanceof ClassType) {
+ final ClassType qualifierType = (ClassType)object;
+ referenceType = debugProcess.findClass(context, qualifierType.name(), context.getClassLoader());
+ }
+ else {
+ final String className = myClassName != null? myClassName.getName(debugProcess) : null;
+ if (className != null) {
+ referenceType = debugProcess.findClass(context, className, context.getClassLoader());
+ }
+ }
+
+ if (referenceType == null) {
+ throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("evaluation.error.cannot.evaluate.qualifier", myMethodName))
+ );
+ }
+ final String signature = myMethodSignature != null ? myMethodSignature.getName(debugProcess) : null;
+ final String methodName = DebuggerUtilsEx.methodName(referenceType.name(), myMethodName, signature);
+ if (object instanceof ClassType) {
+ if(referenceType instanceof ClassType) {
+ Method jdiMethod;
+ if(myMethodSignature != null) {
+ jdiMethod = ((ClassType)referenceType).concreteMethodByName(myMethodName, myMethodSignature.getName(debugProcess));
+ }
+ else {
+ List list = referenceType.methodsByName(myMethodName);
+ jdiMethod = (Method)(list.size() > 0 ? list.get(0) : null);
+ }
+ if (jdiMethod != null && jdiMethod.isStatic()) {
+ return debugProcess.invokeMethod(context, (ClassType)referenceType, jdiMethod, args);
+ }
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.static.method", methodName));
+ }
+ // object should be an ObjectReference
+ final ObjectReference objRef = (ObjectReference)object;
+ ReferenceType _refType = referenceType;
+ if (requiresSuperObject && (referenceType instanceof ClassType)) {
+ _refType = ((ClassType)referenceType).superclass();
+ }
+ final Method jdiMethod = DebuggerUtilsEx.findMethod(_refType, myMethodName, signature);
+ if (jdiMethod == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.instance.method", methodName));
+ }
+ if (requiresSuperObject) {
+ return debugProcess.invokeInstanceMethod(context, objRef, jdiMethod, args, ObjectReference.INVOKE_NONVIRTUAL);
+ }
+ return debugProcess.invokeMethod(context, objRef, jdiMethod, args);
+ }
+ catch (Exception e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewArrayInstanceEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewArrayInstanceEvaluator.java
new file mode 100644
index 0000000..a0f9033
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewArrayInstanceEvaluator.java
@@ -0,0 +1,150 @@
+/*
+ * 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 NewArrayInstanceEvaluator
+ * created Jun 27, 2001
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+class NewArrayInstanceEvaluator implements Evaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.NewArrayInstanceEvaluator");
+ private final Evaluator myArrayTypeEvaluator;
+ private Evaluator myDimensionEvaluator = null;
+ private Evaluator myInitializerEvaluator = null;
+
+ /**
+ * either dimensionEvaluator or initializerEvaluators must be null!
+ */
+ public NewArrayInstanceEvaluator(Evaluator arrayTypeEvaluator, Evaluator dimensionEvaluator, Evaluator initializerEvaluator) {
+ myArrayTypeEvaluator = arrayTypeEvaluator;
+ myDimensionEvaluator = dimensionEvaluator;
+ myInitializerEvaluator = initializerEvaluator;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+// throw new EvaluateException("Creating new array instances is not supported yet", true);
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+ Object obj = myArrayTypeEvaluator.evaluate(context);
+ if (!(obj instanceof ArrayType)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.array.type.expected"));
+ }
+ ArrayType arrayType = (ArrayType)obj;
+ int dimension;
+ Object[] initialValues = null;
+ if (myDimensionEvaluator != null) {
+ Object o = myDimensionEvaluator.evaluate(context);
+ if (!(o instanceof Value && DebuggerUtilsEx.isNumeric((Value)o))) {
+ throw EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("evaluation.error.array.dimention.numeric.value.expected")
+ );
+ }
+ PrimitiveValue value = (PrimitiveValue)o;
+ dimension = value.intValue();
+ }
+ else { // myInitializerEvaluator must not be null
+ Object o = myInitializerEvaluator.evaluate(context);
+ if (!(o instanceof Object[])) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.array.initializer"));
+ }
+ initialValues = (Object[])o;
+ dimension = initialValues.length;
+ }
+ ArrayReference arrayReference = debugProcess.newInstance(arrayType, dimension);
+ if (initialValues != null && initialValues.length > 0) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Setting initial values: dimension = "+dimension + "; array size is "+initialValues.length);
+ }
+ setInitialValues(arrayReference, initialValues, context);
+ }
+ return arrayReference;
+ }
+
+ private static void setInitialValues(ArrayReference arrayReference, Object[] values, EvaluationContextImpl context) throws EvaluateException {
+ ArrayType type = (ArrayType)arrayReference.referenceType();
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+ try {
+ if (type.componentType() instanceof ArrayType) {
+ ArrayType componentType = (ArrayType)type.componentType();
+ int length = arrayReference.length();
+ for (int idx = 0; idx < length; idx++) {
+ ArrayReference componentArray = (ArrayReference)arrayReference.getValue(idx);
+ Object[] componentArrayValues = (Object[])values[idx];
+ if (componentArray == null) {
+ componentArray = debugProcess.newInstance(componentType, componentArrayValues.length);
+ arrayReference.setValue(idx, componentArray);
+ }
+ setInitialValues(componentArray, componentArrayValues, context);
+ }
+ }
+ else {
+ if (values.length > 0) {
+ arrayReference.setValues(new ArrayList(Arrays.asList(values)));
+ }
+ }
+ }
+ catch (ClassNotLoadedException ex) {
+ final ReferenceType referenceType;
+ try {
+ referenceType = context.isAutoLoadClasses()? debugProcess.loadClass(context, ex.className(), type.classLoader()) : null;
+ }
+ catch (InvocationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (ClassNotLoadedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InvalidTypeException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ if (referenceType != null) {
+ setInitialValues(arrayReference, values, context);
+ }
+ else {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.class.not.loaded", ex.className()));
+ }
+ }
+ catch (InvalidTypeException ex) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.array.initializer.type"));
+ }
+ catch (IndexOutOfBoundsException ex) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.array.size"));
+ }
+ catch (ClassCastException ex) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.initialize.array"));
+ }
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java
new file mode 100644
index 0000000..c3e7f81
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java
@@ -0,0 +1,87 @@
+/*
+ * 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 NewArrayInstanceEvaluator
+ * created Jun 27, 2001
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JVMName;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.DebuggerBundle;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+class NewClassInstanceEvaluator implements Evaluator {
+ private final Evaluator myClassTypeEvaluator;
+ private final JVMName myConstructorSignature;
+ private final Evaluator[] myParamsEvaluators;
+
+ public NewClassInstanceEvaluator(Evaluator classTypeEvaluator, JVMName constructorSignature, Evaluator[] argumentEvaluators) {
+ myClassTypeEvaluator = classTypeEvaluator;
+ myConstructorSignature = constructorSignature;
+ myParamsEvaluators = argumentEvaluators;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+ Object obj = myClassTypeEvaluator.evaluate(context);
+ if (!(obj instanceof ClassType)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.class.type"));
+ }
+ ClassType classType = (ClassType)obj;
+ // find constructor
+ Method method = DebuggerUtilsEx.findMethod(classType, "<init>", myConstructorSignature.getName(debugProcess));
+ if (method == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("evaluation.error.cannot.resolve.constructor", myConstructorSignature.getDisplayName(debugProcess)));
+ }
+ // evaluate arguments
+ List<Object> arguments;
+ if (myParamsEvaluators != null) {
+ arguments = new ArrayList<Object>(myParamsEvaluators.length);
+ for (Evaluator evaluator : myParamsEvaluators) {
+ arguments.add(evaluator.evaluate(context));
+ }
+ }
+ else {
+ arguments = Collections.emptyList();
+ }
+ ObjectReference objRef;
+ try {
+ objRef = debugProcess.newInstance(context, classType, method, arguments);
+ }
+ catch (EvaluateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ return objRef;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/PostfixOperationEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/PostfixOperationEvaluator.java
new file mode 100644
index 0000000..203256c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/PostfixOperationEvaluator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+
+public class PostfixOperationEvaluator implements Evaluator{
+ private final Evaluator myOperandEvaluator;
+
+ private final Evaluator myIncrementImpl;
+
+ private Modifier myModifier;
+
+ public PostfixOperationEvaluator(Evaluator operandEvaluator, Evaluator incrementImpl) {
+ myOperandEvaluator = new DisableGC(operandEvaluator);
+ myIncrementImpl = new DisableGC(incrementImpl);
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ final Object value = myOperandEvaluator.evaluate(context);
+ myModifier = myOperandEvaluator.getModifier();
+ Object operationResult = myIncrementImpl.evaluate(context);
+ AssignmentEvaluator.assign(myModifier, operationResult, context);
+ return value;
+ }
+
+ public Modifier getModifier() {
+ return myModifier;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/SuperEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/SuperEvaluator.java
new file mode 100644
index 0000000..ba126ae
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/SuperEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 22, 2007
+ */
+public class SuperEvaluator extends ThisEvaluator{
+
+ public SuperEvaluator(final int iterations) {
+ super(iterations);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/SyntheticVariableEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/SyntheticVariableEvaluator.java
new file mode 100644
index 0000000..e96884c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/SyntheticVariableEvaluator.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+
+/**
+ * @author lex
+ */
+public class SyntheticVariableEvaluator implements Evaluator{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.SyntheticVariableEvaluator");
+
+ private final CodeFragmentEvaluator myCodeFragmentEvaluator;
+ private final String myLocalName;
+
+ public SyntheticVariableEvaluator(CodeFragmentEvaluator codeFragmentEvaluator, String localName) {
+ myCodeFragmentEvaluator = codeFragmentEvaluator;
+ myLocalName = localName;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ return myCodeFragmentEvaluator.getValue(myLocalName, context.getDebugProcess().getVirtualMachineProxy());
+ }
+
+ public Modifier getModifier() {
+ return new Modifier() {
+ public boolean canInspect() {
+ return false;
+ }
+
+ public boolean canSetValue() {
+ return false;
+ }
+
+ public void setValue(Value value) throws EvaluateException {
+ myCodeFragmentEvaluator.setValue(myLocalName, value);
+ }
+
+ public Type getExpectedType() {
+ LOG.assertTrue(false);
+ return null;
+ }
+
+ public NodeDescriptorImpl getInspectItem(Project project) {
+ return null;
+ }
+ };
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThisEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThisEvaluator.java
new file mode 100644
index 0000000..c9e26c3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThisEvaluator.java
@@ -0,0 +1,82 @@
+/*
+ * 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 ThisEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.Field;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class ThisEvaluator implements Evaluator {
+ private final int myIterations;
+
+ public ThisEvaluator() {
+ myIterations = 0;
+ }
+
+ public ThisEvaluator(int iterations) {
+ myIterations = iterations;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Value objRef = context.getThisObject();
+ if(myIterations > 0) {
+ ObjectReference thisRef = (ObjectReference)objRef;
+ for (int idx = 0; idx < myIterations && thisRef != null; idx++) {
+ thisRef = getOuterObject(thisRef);
+ }
+ objRef = thisRef;
+ }
+ if(objRef == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.this.not.avalilable"));
+ }
+ return objRef;
+ }
+
+ @Nullable
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static ObjectReference getOuterObject(ObjectReference objRef) {
+ if (objRef == null) {
+ return null;
+ }
+ List<Field> list = objRef.referenceType().fields();
+ for (final Field field : list) {
+ final String name = field.name();
+ if (name != null && name.startsWith("this$")) {
+ final ObjectReference rv = (ObjectReference)objRef.getValue(field);
+ if (rv != null) {
+ return rv;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TypeCastEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TypeCastEvaluator.java
new file mode 100644
index 0000000..482373d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TypeCastEvaluator.java
@@ -0,0 +1,84 @@
+/*
+ * 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 TypeCastEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.CharValue;
+import com.sun.jdi.PrimitiveValue;
+import com.sun.jdi.Value;
+
+public class TypeCastEvaluator implements Evaluator {
+ private final Evaluator myOperandEvaluator;
+ private final String myCastType;
+ private final boolean myIsPrimitive;
+
+ public TypeCastEvaluator(Evaluator operandEvaluator, String castType, boolean isPrimitive) {
+ myOperandEvaluator = operandEvaluator;
+ myCastType = castType;
+ myIsPrimitive = isPrimitive;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Value value = (Value)myOperandEvaluator.evaluate(context);
+ if (value == null) {
+ if (myIsPrimitive) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.cast.null", myCastType));
+ }
+ return null;
+ }
+ VirtualMachineProxyImpl vm = context.getDebugProcess().getVirtualMachineProxy();
+ if (DebuggerUtilsEx.isInteger(value)) {
+ value = DebuggerUtilsEx.createValue(vm, myCastType, ((PrimitiveValue)value).longValue());
+ if (value == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.cast.numeric", myCastType));
+ }
+ }
+ else if (DebuggerUtilsEx.isNumeric(value)) {
+ value = DebuggerUtilsEx.createValue(vm, myCastType, ((PrimitiveValue)value).doubleValue());
+ if (value == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.cast.numeric", myCastType));
+ }
+ }
+ else if (value instanceof BooleanValue) {
+ value = DebuggerUtilsEx.createValue(vm, myCastType, ((BooleanValue)value).booleanValue());
+ if (value == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.cast.boolean", myCastType));
+ }
+ }
+ else if (value instanceof CharValue) {
+ value = DebuggerUtilsEx.createValue(vm, myCastType, ((CharValue)value).charValue());
+ if (value == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.cast.char", myCastType));
+ }
+ }
+ return value;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TypeEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TypeEvaluator.java
new file mode 100644
index 0000000..1ba0457
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TypeEvaluator.java
@@ -0,0 +1,54 @@
+/*
+ * 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 TypeEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JVMName;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.sun.jdi.ReferenceType;
+
+public class TypeEvaluator implements Evaluator {
+ private final JVMName myTypeName;
+
+ public TypeEvaluator(JVMName typeName) {
+ myTypeName = typeName;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ /**
+ * @return ReferenceType in the target VM, with the given fully qualified name
+ */
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ DebugProcessImpl debugProcess = context.getDebugProcess();
+ String typeName = myTypeName.getName(debugProcess);
+ final ReferenceType type = debugProcess.findClass(context, typeName, context.getClassLoader());
+ if (type == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.class.not.loaded", typeName));
+ }
+ return type;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java
new file mode 100644
index 0000000..cb4505b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2010 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.containers.HashMap;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 8, 2010
+ */
+public class UnBoxingEvaluator implements Evaluator{
+ private final Evaluator myOperand;
+ private static final Map<String, Pair<String, String>> TYPES_TO_CONVERSION_METHOD_MAP = new HashMap<String, Pair<String, String>>();
+ static {
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Boolean", new Pair<String, String>("booleanValue", "()Z"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Byte", new Pair<String, String>("byteValue", "()B"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Character", new Pair<String, String>("charValue", "()C"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Short", new Pair<String, String>("shortValue", "()S"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Integer", new Pair<String, String>("intValue", "()I"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Long", new Pair<String, String>("longValue", "()J"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Float", new Pair<String, String>("floatValue", "()F"));
+ TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Double", new Pair<String, String>("doubleValue", "()D"));
+ }
+
+ public static boolean isTypeUnboxable(String typeName) {
+ return TYPES_TO_CONVERSION_METHOD_MAP.containsKey(typeName);
+ }
+
+ public UnBoxingEvaluator(Evaluator operand) {
+ myOperand = new DisableGC(operand);
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ final Value result = (Value)myOperand.evaluate(context);
+ if (result == null) {
+ throw new EvaluateException("java.lang.NullPointerException: cannot unbox null value");
+ }
+ if (result instanceof ObjectReference) {
+ final String valueTypeName = result.type().name();
+ final Pair<String, String> pair = TYPES_TO_CONVERSION_METHOD_MAP.get(valueTypeName);
+ if (pair != null) {
+ return convertToPrimitive(context, (ObjectReference)result, pair.getFirst(), pair.getSecond());
+ }
+ }
+ return result;
+ }
+
+ @Nullable
+ public Modifier getModifier() {
+ return null;
+ }
+
+ private static Value convertToPrimitive(EvaluationContextImpl context, ObjectReference value, final String conversionMethodName,
+ String conversionMethodSignature) throws EvaluateException {
+ final DebugProcessImpl process = context.getDebugProcess();
+ final ClassType wrapperClass = (ClassType)value.referenceType();
+ final List<Method> methods = wrapperClass.methodsByName(conversionMethodName, conversionMethodSignature);
+ if (methods.size() == 0) {
+ throw new EvaluateException("Cannot convert to primitive value of type " + value.type() + ": Unable to find method " +
+ conversionMethodName + conversionMethodSignature);
+ }
+
+ final Method method = methods.get(0);
+
+ return process.invokeMethod(context, value, method, new ArrayList());
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnaryExpressionEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnaryExpressionEvaluator.java
new file mode 100644
index 0000000..77f3c34
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnaryExpressionEvaluator.java
@@ -0,0 +1,91 @@
+/*
+ * 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 UnaryExpressionEvaluator
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.tree.IElementType;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.PrimitiveValue;
+import com.sun.jdi.Value;
+
+class UnaryExpressionEvaluator implements Evaluator {
+ private final IElementType myOperationType;
+ private final String myExpectedType;
+ private final Evaluator myOperandEvaluator;
+ private final String myOperationText;
+
+ public UnaryExpressionEvaluator(IElementType operationType, String expectedType, Evaluator operandEvaluator, final String operationText) {
+ myOperationType = operationType;
+ myExpectedType = expectedType;
+ myOperandEvaluator = operandEvaluator;
+ myOperationText = operationText;
+ }
+
+ public Modifier getModifier() {
+ return null;
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Value operand = (Value)myOperandEvaluator.evaluate(context);
+ VirtualMachineProxyImpl vm = context.getDebugProcess().getVirtualMachineProxy();
+ if (myOperationType == JavaTokenType.PLUS) {
+ if (DebuggerUtilsEx.isNumeric(operand)) {
+ return operand;
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.numeric.expected"));
+ }
+ else if (myOperationType == JavaTokenType.MINUS) {
+ if (DebuggerUtilsEx.isInteger(operand)) {
+ long v = ((PrimitiveValue)operand).longValue();
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, -v);
+ }
+ if (DebuggerUtilsEx.isNumeric(operand)) {
+ double v = ((PrimitiveValue)operand).doubleValue();
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, -v);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.numeric.expected"));
+ }
+ else if (myOperationType == JavaTokenType.TILDE) {
+ if (DebuggerUtilsEx.isInteger(operand)) {
+ long v = ((PrimitiveValue)operand).longValue();
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, ~v);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.integer.expected"));
+ }
+ else if (myOperationType == JavaTokenType.EXCL) {
+ if (operand instanceof BooleanValue) {
+ boolean v = ((BooleanValue)operand).booleanValue();
+ return DebuggerUtilsEx.createValue(vm, myExpectedType, !v);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.boolean.expected"));
+ }
+
+ throw EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("evaluation.error.operation.not.supported", myOperationText)
+ );
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/WhileStatementEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/WhileStatementEvaluator.java
new file mode 100644
index 0000000..82f4ca8
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/WhileStatementEvaluator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.openapi.util.Comparing;
+import com.sun.jdi.BooleanValue;
+
+/**
+ * @author lex
+ */
+public class WhileStatementEvaluator implements Evaluator {
+ private final Evaluator myConditionEvaluator;
+ private final Evaluator myBodyEvaluator;
+ private final String myLabelName;
+
+ public WhileStatementEvaluator(Evaluator conditionEvaluator, Evaluator bodyEvaluator, String labelName) {
+ myConditionEvaluator = new DisableGC(conditionEvaluator);
+ myBodyEvaluator = new DisableGC(bodyEvaluator);
+ myLabelName = labelName;
+ }
+
+ public Modifier getModifier() {
+ return myConditionEvaluator.getModifier();
+ }
+
+ public Object evaluate(EvaluationContextImpl context) throws EvaluateException {
+ Object value;
+ while (true) {
+ value = myConditionEvaluator.evaluate(context);
+ if (!(value instanceof BooleanValue)) {
+ throw EvaluateExceptionUtil.BOOLEAN_EXPECTED;
+ }
+ else {
+ if (!((BooleanValue)value).booleanValue()) {
+ break;
+ }
+ }
+ try {
+ myBodyEvaluator.evaluate(context);
+ }
+ catch (BreakException e) {
+ if (Comparing.equal(e.getLabelName(), myLabelName)) {
+ break;
+ }
+ else {
+ throw e;
+ }
+ }
+ catch (ContinueException e) {
+ if (!Comparing.equal(e.getLabelName(), myLabelName)) {
+ throw e;
+ }
+ }
+ }
+
+ return value;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java
new file mode 100644
index 0000000..038ff6d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.events;
+
+import com.intellij.debugger.impl.DebuggerTaskImpl;
+
+/**
+ * @author lex
+ */
+public abstract class DebuggerCommandImpl extends DebuggerTaskImpl {
+ protected abstract void action() throws Exception;
+
+ protected void commandCancelled() {
+ }
+
+ public Priority getPriority() {
+ return Priority.LOW;
+ }
+
+ public final void notifyCancelled() {
+ try {
+ commandCancelled();
+ }
+ finally {
+ release();
+ }
+ }
+
+ public final void run() throws Exception{
+ try {
+ action();
+ }
+ finally {
+ release();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java
new file mode 100644
index 0000000..aaf3631
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.events;
+
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.SuspendManager;
+import com.intellij.debugger.engine.SuspendManagerUtil;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.ObjectCollectedException;
+
+public abstract class DebuggerContextCommandImpl extends SuspendContextCommandImpl {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.events.DebuggerContextCommandImpl");
+
+ private final DebuggerContextImpl myDebuggerContext;
+
+ protected DebuggerContextCommandImpl(DebuggerContextImpl debuggerContext) {
+ super(debuggerContext.getSuspendContext());
+ myDebuggerContext = debuggerContext;
+ }
+
+ public final DebuggerContextImpl getDebuggerContext() {
+ return myDebuggerContext;
+ }
+
+ public final void contextAction() throws Exception {
+ final SuspendManager suspendManager = myDebuggerContext.getDebugProcess().getSuspendManager();
+
+ final ThreadReferenceProxyImpl debuggerContextThread = myDebuggerContext.getThreadProxy();
+ final boolean isSuspendedByContext;
+ try {
+ isSuspendedByContext = suspendManager.isSuspended(debuggerContextThread);
+ }
+ catch (ObjectCollectedException e) {
+ notifyCancelled();
+ return;
+ }
+ if (isSuspendedByContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Context thread " + getSuspendContext().getThread());
+ LOG.debug("Debug thread" + debuggerContextThread);
+ }
+ threadAction();
+ }
+ else {
+ // there are no suspend context currently registered
+ SuspendContextImpl suspendContextForThread = SuspendManagerUtil.findContextByThread(suspendManager, debuggerContextThread);
+ if(suspendContextForThread != null) {
+ suspendContextForThread.postponeCommand(this);
+ }
+ else {
+ notifyCancelled();
+ }
+ }
+ }
+
+ abstract public void threadAction ();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java
new file mode 100644
index 0000000..f3ad395
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.events;
+
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.Stack;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Feb 24, 2004
+ * Time: 7:01:31 PM
+ * Performs contextAction when evaluation is available in suspend context
+ */
+public abstract class SuspendContextCommandImpl extends DebuggerCommandImpl {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendContextCommand");
+ private final SuspendContextImpl mySuspendContext;
+
+ protected SuspendContextCommandImpl(SuspendContextImpl suspendContext) {
+ mySuspendContext = suspendContext;
+ }
+
+ public abstract void contextAction() throws Exception;
+
+ public final void action() throws Exception {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("trying " + this);
+ }
+
+ final SuspendContextImpl suspendContext = getSuspendContext();
+
+ if (suspendContext == null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("skip processing - context is null " + this);
+ }
+ notifyCancelled();
+ return;
+ }
+
+ if(suspendContext.myInProgress) {
+ suspendContext.postponeCommand(this);
+ }
+ else {
+ try {
+ if(!suspendContext.isResumed()) {
+ suspendContext.myInProgress = true;
+ contextAction();
+ }
+ else {
+ notifyCancelled();
+ }
+ }
+ finally{
+ suspendContext.myInProgress = false;
+ SuspendContextCommandImpl postponed = suspendContext.pollPostponedCommand();
+ if (postponed != null) {
+ final Stack<SuspendContextCommandImpl> stack = new Stack<SuspendContextCommandImpl>();
+ while (postponed != null) {
+ stack.push(postponed);
+ postponed = suspendContext.pollPostponedCommand();
+ }
+ final DebuggerManagerThreadImpl managerThread = suspendContext.getDebugProcess().getManagerThread();
+ while (!stack.isEmpty()) {
+ managerThread.pushBack(stack.pop());
+ }
+ }
+ }
+ }
+ }
+
+ public SuspendContextImpl getSuspendContext() {
+ return mySuspendContext;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/requests/LocatableEventRequestor.java b/java/debugger/impl/src/com/intellij/debugger/engine/requests/LocatableEventRequestor.java
new file mode 100644
index 0000000..db64d1d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/requests/LocatableEventRequestor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.requests;
+
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.requests.Requestor;
+import com.sun.jdi.event.LocatableEvent;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jun 27, 2003
+ * Time: 8:05:52 PM
+ * To change this template use Options | File Templates.
+ */
+public interface LocatableEventRequestor extends Requestor {
+ /**
+ * @returns true if requesto was hit by the event, false otherwise
+ */
+ boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) throws EventProcessingException;
+
+ /**
+ * @return either DebuggerSettings.SUSPEND_NONE or DebuggerSettings.SUSPEND_ALL or DebuggerSettings.SUSPEND_THREAD
+ */
+ String getSuspendPolicy();
+
+ class EventProcessingException extends Exception {
+ private final String myTitle;
+
+ public EventProcessingException(String title, String message, Throwable cause) {
+ super(message, cause);
+ myTitle = title;
+ }
+
+ public String getTitle() {
+ return myTitle;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/requests/MethodReturnValueWatcher.java b/java/debugger/impl/src/com/intellij/debugger/engine/requests/MethodReturnValueWatcher.java
new file mode 100644
index 0000000..d56c31e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/requests/MethodReturnValueWatcher.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.requests;
+
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.ArrayUtil;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+import com.sun.jdi.event.MethodExitEvent;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.MethodExitRequest;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Nov 23, 2006
+ */
+public class MethodReturnValueWatcher {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.requests.MethodReturnValueWatcher");
+ private @Nullable Method myLastExecutedMethod;
+ private @Nullable Value myLastMethodReturnValue;
+ private @Nullable MethodExitRequest myRequest;
+ private java.lang.reflect.Method myReturnValueMethod;
+ private volatile boolean myEnabled;
+ private boolean myFeatureEnabled;
+ private final EventRequestManager myRequestManager;
+
+ public MethodReturnValueWatcher(EventRequestManager requestManager) {
+ myRequestManager = requestManager;
+ myFeatureEnabled = DebuggerSettings.getInstance().WATCH_RETURN_VALUES;
+ }
+
+ public boolean processMethodExitEvent(MethodExitEvent event) {
+ if (event.request() != myRequest) {
+ return false;
+ }
+ try {
+ final Method method = event.method();
+ //myLastMethodReturnValue = event.returnValue();
+ try {
+ if (myReturnValueMethod == null) {
+ //noinspection HardCodedStringLiteral
+ myReturnValueMethod = MethodExitEvent.class.getDeclaredMethod("returnValue", ArrayUtil.EMPTY_CLASS_ARRAY);
+ }
+ final Value retVal = (Value)myReturnValueMethod.invoke(event);
+
+ if (method == null || !"void".equals(method.returnTypeName())) {
+ // remember methods with non-void return types only
+ myLastExecutedMethod = method;
+ myLastMethodReturnValue = retVal;
+ }
+ }
+ catch (NoSuchMethodException ignored) {
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ catch (InvocationTargetException ignored) {
+ }
+ }
+ catch (UnsupportedOperationException ex) {
+ LOG.error(ex);
+ }
+ return true;
+ }
+
+
+ @Nullable
+ public Method getLastExecutedMethod() {
+ return myLastExecutedMethod;
+ }
+
+ @Nullable
+ public Value getLastMethodReturnValue() {
+ return myLastMethodReturnValue;
+ }
+
+ public boolean isFeatureEnabled() {
+ return myFeatureEnabled;
+ }
+
+ public boolean isEnabled() {
+ return myEnabled;
+ }
+
+ public void setFeatureEnabled(final boolean featureEnabled) {
+ myFeatureEnabled = featureEnabled;
+ myLastExecutedMethod = null;
+ myLastMethodReturnValue = null;
+ }
+
+ public void enable(ThreadReference thread) {
+ setTrackingEnabled(true, thread);
+ }
+
+ public void disable() {
+ setTrackingEnabled(false, null);
+ }
+
+ private void setTrackingEnabled(boolean trackingEnabled, final ThreadReference thread) {
+ myEnabled = trackingEnabled;
+ updateRequestState(trackingEnabled && myFeatureEnabled, thread);
+ }
+
+ private void updateRequestState(final boolean enabled, @Nullable final ThreadReference thread) {
+ try {
+ final MethodExitRequest request = myRequest;
+ if (request != null) {
+ myRequest = null;
+ myRequestManager.deleteEventRequest(request);
+ }
+ if (enabled) {
+ myLastExecutedMethod = null;
+ myLastMethodReturnValue = null;
+ myRequest = createRequest(thread);
+ myRequest.enable();
+ }
+ }
+ catch (ObjectCollectedException ignored) {
+ }
+ }
+
+ private MethodExitRequest createRequest(@Nullable final ThreadReference thread) {
+ final MethodExitRequest request = myRequestManager.createMethodExitRequest();
+ request.setSuspendPolicy(EventRequest.SUSPEND_NONE);
+ if (thread != null) {
+ request.addThreadFilter(thread);
+ }
+ return request;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/requests/RequestManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/requests/RequestManagerImpl.java
new file mode 100644
index 0000000..9d10487
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/engine/requests/RequestManagerImpl.java
@@ -0,0 +1,471 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.engine.requests;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.*;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.debugger.requests.RequestManager;
+import com.intellij.debugger.requests.Requestor;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.FilteredRequestor;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.PsiClass;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.util.containers.HashMap;
+import com.sun.jdi.*;
+import com.sun.jdi.event.ClassPrepareEvent;
+import com.sun.jdi.request.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author lex
+ * Date: May 6, 2003
+ * Time: 5:32:38 PM
+ */
+public class RequestManagerImpl extends DebugProcessAdapterImpl implements RequestManager {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.RequestManagerImpl");
+
+ private static final Key CLASS_NAME = Key.create("ClassName");
+ private static final Key<Requestor> REQUESTOR = Key.create("Requestor");
+
+ private final DebugProcessImpl myDebugProcess;
+ private final Map<Requestor, String> myRequestWarnings = new HashMap<Requestor, String>();
+
+ private final Map<Requestor, Set<EventRequest>> myRequestorToBelongedRequests = new HashMap<Requestor, Set<EventRequest>>();
+ private EventRequestManager myEventRequestManager;
+ private @Nullable ThreadReference myFilterThread;
+
+ public RequestManagerImpl(DebugProcessImpl debugProcess) {
+ myDebugProcess = debugProcess;
+ myDebugProcess.addDebugProcessListener(this);
+ }
+
+
+ public EventRequestManager getVMRequestManager() {
+ return myEventRequestManager;
+ }
+
+ @Nullable
+ public ThreadReference getFilterThread() {
+ return myFilterThread;
+ }
+
+ public void setFilterThread(@Nullable final ThreadReference filterThread) {
+ myFilterThread = filterThread;
+ }
+
+ public Set<EventRequest> findRequests(Requestor requestor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final Set<EventRequest> requestSet = myRequestorToBelongedRequests.get(requestor);
+ if (requestSet == null) {
+ return Collections.emptySet();
+ }
+ return Collections.unmodifiableSet(requestSet);
+ }
+
+ public Requestor findRequestor(EventRequest request) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return request != null? (Requestor)request.getProperty(REQUESTOR) : null;
+ }
+
+ private static void addClassFilter(EventRequest request, String pattern){
+ if(request instanceof AccessWatchpointRequest){
+ ((AccessWatchpointRequest) request).addClassFilter(pattern);
+ }
+ else if(request instanceof ExceptionRequest){
+ ((ExceptionRequest) request).addClassFilter(pattern);
+ }
+ else if(request instanceof MethodEntryRequest) {
+ ((MethodEntryRequest)request).addClassFilter(pattern);
+ }
+ else if(request instanceof MethodExitRequest) {
+ ((MethodExitRequest)request).addClassFilter(pattern);
+ }
+ else if(request instanceof ModificationWatchpointRequest) {
+ ((ModificationWatchpointRequest)request).addClassFilter(pattern);
+ }
+ else if(request instanceof WatchpointRequest) {
+ ((WatchpointRequest)request).addClassFilter(pattern);
+ }
+ }
+
+ private static void addClassExclusionFilter(EventRequest request, String pattern){
+ if(request instanceof AccessWatchpointRequest){
+ ((AccessWatchpointRequest) request).addClassExclusionFilter(pattern);
+ }
+ else if(request instanceof ExceptionRequest){
+ ((ExceptionRequest) request).addClassExclusionFilter(pattern);
+ }
+ else if(request instanceof MethodEntryRequest) {
+ ((MethodEntryRequest)request).addClassExclusionFilter(pattern);
+ }
+ else if(request instanceof MethodExitRequest) {
+ ((MethodExitRequest)request).addClassExclusionFilter(pattern);
+ }
+ else if(request instanceof ModificationWatchpointRequest) {
+ ((ModificationWatchpointRequest)request).addClassExclusionFilter(pattern);
+ }
+ else if(request instanceof WatchpointRequest) {
+ ((WatchpointRequest)request).addClassExclusionFilter(pattern);
+ }
+ }
+
+ private void addLocatableRequest(FilteredRequestor requestor, EventRequest request) {
+ if(DebuggerSettings.SUSPEND_ALL.equals(requestor.SUSPEND_POLICY)) {
+ request.setSuspendPolicy(EventRequest.SUSPEND_ALL);
+ }
+ else {
+ //when requestor.SUSPEND_POLICY == SUSPEND_NONE
+ //we should pause thread in order to evaluate conditions
+ request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
+ }
+
+ if (requestor.COUNT_FILTER_ENABLED && requestor.COUNT_FILTER > 0) {
+ request.addCountFilter(requestor.COUNT_FILTER);
+ }
+
+ if (requestor.CLASS_FILTERS_ENABLED && !(request instanceof BreakpointRequest) /*no built-in class filters support for breakpoint requests*/ ) {
+ ClassFilter[] classFilters = requestor.getClassFilters();
+ for (final ClassFilter filter : classFilters) {
+ if (!filter.isEnabled()) {
+ continue;
+ }
+ final JVMName jvmClassName = ApplicationManager.getApplication().runReadAction(new Computable<JVMName>() {
+ public JVMName compute() {
+ PsiClass psiClass =
+ DebuggerUtilsEx.findClass(filter.getPattern(), myDebugProcess.getProject(), myDebugProcess.getSearchScope());
+ if (psiClass == null) {
+ return null;
+ }
+ return JVMNameUtil.getJVMQualifiedName(psiClass);
+ }
+ });
+ String pattern = filter.getPattern();
+ try {
+ if (jvmClassName != null) {
+ pattern = jvmClassName.getName(myDebugProcess);
+ }
+ }
+ catch (EvaluateException e) {
+ }
+
+ addClassFilter(request, pattern);
+ }
+
+ final ClassFilter[] iclassFilters = requestor.getClassExclusionFilters();
+ for (ClassFilter filter : iclassFilters) {
+ if (filter.isEnabled()) {
+ addClassExclusionFilter(request, filter.getPattern());
+ }
+ }
+ }
+
+ registerRequestInternal(requestor, request);
+ }
+
+ public void registerRequestInternal(final Requestor requestor, final EventRequest request) {
+ registerRequest(requestor, request);
+ request.putProperty(REQUESTOR, requestor);
+ }
+
+ private void registerRequest(Requestor requestor, EventRequest request) {
+ Set<EventRequest> reqSet = myRequestorToBelongedRequests.get(requestor);
+ if(reqSet == null) {
+ reqSet = new HashSet<EventRequest>();
+ myRequestorToBelongedRequests.put(requestor, reqSet);
+ }
+ reqSet.add(request);
+
+ }
+
+ // requests creation
+ public ClassPrepareRequest createClassPrepareRequest(ClassPrepareRequestor requestor, String pattern) {
+ ClassPrepareRequest classPrepareRequest = myEventRequestManager.createClassPrepareRequest();
+ classPrepareRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
+ classPrepareRequest.addClassFilter(pattern);
+ classPrepareRequest.putProperty(CLASS_NAME, pattern);
+
+ registerRequestInternal(requestor, classPrepareRequest);
+ return classPrepareRequest;
+ }
+
+ public ExceptionRequest createExceptionRequest(FilteredRequestor requestor, ReferenceType referenceType, boolean notifyCaught, boolean notifyUnCaught) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ ExceptionRequest req = myEventRequestManager.createExceptionRequest(referenceType, notifyCaught, notifyUnCaught);
+ addLocatableRequest(requestor, req);
+ return req;
+ }
+
+ public MethodEntryRequest createMethodEntryRequest(FilteredRequestor requestor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ MethodEntryRequest req = myEventRequestManager.createMethodEntryRequest();
+ addLocatableRequest(requestor, req);
+ return req;
+ }
+
+ public MethodExitRequest createMethodExitRequest(FilteredRequestor requestor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ MethodExitRequest req = myEventRequestManager.createMethodExitRequest();
+ addLocatableRequest(requestor, req);
+ return req;
+ }
+
+ public BreakpointRequest createBreakpointRequest(FilteredRequestor requestor, Location location) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ BreakpointRequest req = myEventRequestManager.createBreakpointRequest(location);
+ addLocatableRequest(requestor, req);
+ myRequestWarnings.remove(requestor);
+ return req;
+ }
+
+ public AccessWatchpointRequest createAccessWatchpointRequest(FilteredRequestor requestor, Field field) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ AccessWatchpointRequest req = myEventRequestManager.createAccessWatchpointRequest(field);
+ addLocatableRequest(requestor, req);
+ return req;
+ }
+
+ public ModificationWatchpointRequest createModificationWatchpointRequest(FilteredRequestor requestor, Field field) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ ModificationWatchpointRequest req = myEventRequestManager.createModificationWatchpointRequest(field);
+ addLocatableRequest(requestor, req);
+ return req;
+ }
+
+ public void deleteRequest(Requestor requestor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if(!myDebugProcess.isAttached()) {
+ return;
+ }
+ final Set<EventRequest> requests = myRequestorToBelongedRequests.remove(requestor);
+ if(requests == null) {
+ return;
+ }
+ for (final EventRequest request : requests) {
+ try {
+ final Requestor targetRequestor = (Requestor)request.getProperty(REQUESTOR);
+ if (targetRequestor != requestor) {
+ // the same request may be assigned to more than one requestor, but
+ // there is only one 'targetRequestor' for each request, so if target requestor and requestor being processed are different,
+ // should clear also the mapping targetRequestor->request
+ final Set<EventRequest> allTargetRequestorRequests = myRequestorToBelongedRequests.get(targetRequestor);
+ if (allTargetRequestorRequests != null) {
+ allTargetRequestorRequests.remove(request);
+ if (allTargetRequestorRequests.size() == 0) {
+ myRequestorToBelongedRequests.remove(targetRequestor);
+ }
+ }
+ }
+ myEventRequestManager.deleteEventRequest(request);
+ }
+ catch (InvalidRequestStateException ignored) {
+ // request is already deleted
+ }
+ catch (InternalException e) {
+ if (e.errorCode() == 41) {
+ //event request not found
+ //there could be no requests after hotswap
+ }
+ else {
+ LOG.error(e);
+ }
+ }
+ }
+ }
+
+ public void callbackOnPrepareClasses(final ClassPrepareRequestor requestor, final SourcePosition classPosition) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ ClassPrepareRequest prepareRequest = myDebugProcess.getPositionManager().createPrepareRequest(requestor, classPosition);
+
+ if(prepareRequest == null) {
+ setInvalid(requestor, DebuggerBundle.message("status.invalid.breakpoint.out.of.class"));
+ return;
+ }
+
+ registerRequest(requestor, prepareRequest);
+ prepareRequest.enable();
+ }
+
+ public void callbackOnPrepareClasses(ClassPrepareRequestor requestor, String classOrPatternToBeLoaded) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ ClassPrepareRequest classPrepareRequest = createClassPrepareRequest(requestor, classOrPatternToBeLoaded);
+
+ registerRequest(requestor, classPrepareRequest);
+ classPrepareRequest.enable();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("classOrPatternToBeLoaded = " + classOrPatternToBeLoaded);
+ }
+ }
+
+ public void enableRequest(EventRequest request) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ LOG.assertTrue(findRequestor(request) != null);
+ try {
+ final ThreadReference filterThread = myFilterThread;
+ if (filterThread != null) {
+ if (request instanceof BreakpointRequest) {
+ ((BreakpointRequest)request).addThreadFilter(filterThread);
+ }
+ else if (request instanceof MethodEntryRequest) {
+ ((MethodEntryRequest)request).addThreadFilter(filterThread);
+ }
+ else if (request instanceof MethodExitRequest) {
+ ((MethodExitRequest)request).addThreadFilter(filterThread);
+ }
+ }
+ request.enable();
+ } catch (InternalException e) {
+ if(e.errorCode() == 41) {
+ //event request not found
+ //there could be no requests after hotswap
+ } else {
+ LOG.error(e);
+ }
+ }
+ }
+
+ public void setInvalid(Requestor requestor, String message) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ //deleteRequest(requestor);
+ //myRequestorToBelongedRequests.remove(requestor); // clear any mapping to empty set if any
+ if (!isVerified(requestor)) {
+ myRequestWarnings.put(requestor, message);
+ }
+ }
+
+ public @Nullable String getWarning(Requestor requestor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return myRequestWarnings.get(requestor);
+ }
+
+ public boolean isVerified(Requestor requestor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ for (EventRequest request : findRequests(requestor)) {
+ /*ClassPrepareRequest is added in any case, so do not count it*/
+ if (!(request instanceof ClassPrepareRequest)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void processDetached(DebugProcessImpl process, boolean closedByUser) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myEventRequestManager = null;
+ myRequestWarnings.clear();
+ myRequestorToBelongedRequests.clear();
+ }
+
+ public void processAttached(DebugProcessImpl process) {
+ myEventRequestManager = myDebugProcess.getVirtualMachineProxy().eventRequestManager();
+ // invoke later, so that requests are for sure created only _after_ 'processAttached()' methods of other listeneres are executed
+ process.getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myDebugProcess.getProject()).getBreakpointManager();
+ for (final Breakpoint breakpoint : breakpointManager.getBreakpoints()) {
+ breakpoint.createRequest(myDebugProcess);
+ }
+ }
+ });
+ }
+
+ public void processClassPrepared(final ClassPrepareEvent event) {
+ if (!myDebugProcess.isAttached()) {
+ return;
+ }
+
+ final ReferenceType refType = event.referenceType();
+
+ if (refType instanceof ClassType || refType instanceof InterfaceType) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("signature = " + refType.signature());
+ }
+ ClassPrepareRequestor requestor = (ClassPrepareRequestor)event.request().getProperty(REQUESTOR);
+ if (requestor != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("requestor found " + refType.signature());
+ }
+ requestor.processClassPrepare(myDebugProcess, refType);
+ }
+ }
+ }
+
+ private static interface AllProcessesCommand {
+ void action(DebugProcessImpl process);
+ }
+
+ private static void invoke(Project project, final AllProcessesCommand command) {
+ for (DebuggerSession debuggerSession : (DebuggerManagerEx.getInstanceEx(project)).getSessions()) {
+ final DebugProcessImpl process = debuggerSession.getProcess();
+ if (process != null) {
+ process.getManagerThread().invoke(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ command.action(process);
+ }
+ });
+ }
+ }
+ }
+
+ public static void createRequests(final Breakpoint breakpoint) {
+ invoke(breakpoint.getProject(), new AllProcessesCommand (){
+ public void action(DebugProcessImpl process) {
+ breakpoint.createRequest(process);
+ }
+ });
+ }
+
+ public static void updateRequests(final Breakpoint breakpoint) {
+ invoke(breakpoint.getProject(), new AllProcessesCommand (){
+ public void action(DebugProcessImpl process) {
+ process.getRequestsManager().myRequestWarnings.remove(breakpoint);
+ process.getRequestsManager().deleteRequest(breakpoint);
+ breakpoint.createRequest(process);
+ }
+ });
+ }
+
+ public static void deleteRequests(final Breakpoint breakpoint) {
+ invoke(breakpoint.getProject(), new AllProcessesCommand (){
+ public void action(DebugProcessImpl process) {
+ process.getRequestsManager().myRequestWarnings.remove(breakpoint);
+ process.getRequestsManager().deleteRequest(breakpoint);
+ }
+ });
+ }
+
+ public void clearWarnings() {
+ myRequestWarnings.clear();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java
new file mode 100644
index 0000000..5762517
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+/*
+ * Interface DebuggerContextImpl
+ * @author Jeka
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+
+public final class DebuggerContextImpl implements DebuggerContext {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerContextImpl");
+
+ public static final DebuggerContextImpl EMPTY_CONTEXT = DebuggerContextImpl.createDebuggerContext((DebuggerSession) null, null, null, null);
+
+ private boolean myInitialized;
+
+ private final DebuggerSession myDebuggerSession;
+ private final DebugProcessImpl myDebugProcess;
+ private final SuspendContextImpl mySuspendContext;
+ private final ThreadReferenceProxyImpl myThreadProxy;
+
+ private StackFrameProxyImpl myFrameProxy;
+ private SourcePosition mySourcePosition;
+ private PsiElement myContextElement;
+
+ private DebuggerContextImpl(DebuggerSession session, DebugProcessImpl debugProcess, SuspendContextImpl context, ThreadReferenceProxyImpl threadProxy, StackFrameProxyImpl frameProxy, SourcePosition position, PsiElement contextElement, boolean initialized) {
+ LOG.assertTrue(frameProxy == null || threadProxy == null || threadProxy == frameProxy.threadProxy());
+ LOG.assertTrue(debugProcess == null ? frameProxy == null && threadProxy == null : true);
+ myDebuggerSession = session;
+ myThreadProxy = threadProxy;
+ myFrameProxy = frameProxy;
+ myDebugProcess = debugProcess;
+ mySourcePosition = position;
+ mySuspendContext = context;
+ myContextElement = contextElement;
+ myInitialized = initialized;
+ }
+
+ public DebuggerSession getDebuggerSession() {
+ return myDebuggerSession;
+ }
+
+ public DebugProcessImpl getDebugProcess() {
+ return myDebugProcess;
+ }
+
+ public ThreadReferenceProxyImpl getThreadProxy() {
+ return myThreadProxy;
+ }
+
+ public SuspendContextImpl getSuspendContext() {
+ return mySuspendContext;
+ }
+
+ public Project getProject() {
+ return myDebugProcess != null ? myDebugProcess.getProject() : null;
+ }
+
+ @Nullable
+ public StackFrameProxyImpl getFrameProxy() {
+ LOG.assertTrue(myInitialized);
+ return myFrameProxy;
+ }
+
+ public SourcePosition getSourcePosition() {
+ LOG.assertTrue(myInitialized);
+ return mySourcePosition;
+ }
+
+ public PsiElement getContextElement() {
+ LOG.assertTrue(myInitialized);
+ PsiElement contextElement = myContextElement;
+ if(contextElement != null && !contextElement.isValid()) {
+ myContextElement = ContextUtil.getContextElement(mySourcePosition);
+ }
+ return myContextElement;
+ }
+
+ public EvaluationContextImpl createEvaluationContext(Value thisObject) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return new EvaluationContextImpl(getSuspendContext(), getFrameProxy(), thisObject);
+ }
+
+ public EvaluationContextImpl createEvaluationContext() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ StackFrameProxyImpl frameProxy = getFrameProxy();
+ ObjectReference objectReference;
+ try {
+ objectReference = frameProxy != null ? frameProxy.thisObject() : null;
+ }
+ catch (EvaluateException e) {
+ LOG.info(e);
+ objectReference = null;
+ }
+ return new EvaluationContextImpl(getSuspendContext(), frameProxy, objectReference);
+ }
+
+ public static DebuggerContextImpl createDebuggerContext(DebuggerSession session, SuspendContextImpl context, ThreadReferenceProxyImpl threadProxy, StackFrameProxyImpl frameProxy) {
+ LOG.assertTrue(frameProxy == null || threadProxy == null || threadProxy == frameProxy.threadProxy());
+ LOG.assertTrue(session == null || session.getProcess() != null);
+ return new DebuggerContextImpl(session, session != null ? session.getProcess() : null, context, threadProxy, frameProxy, null, null, context == null);
+ }
+
+ public void initCaches() {
+ if(myInitialized) return;
+
+ myInitialized = true;
+ if(myFrameProxy == null) {
+ if(myThreadProxy != null) {
+ try {
+ myFrameProxy = myThreadProxy.frameCount() > 0 ? myThreadProxy.frame(0) : null;
+ }
+ catch (EvaluateException e) {
+ }
+ }
+ }
+
+ if(myFrameProxy != null) {
+ PsiDocumentManager.getInstance(getProject()).commitAndRunReadAction(new Runnable() {
+ public void run() {
+ if (mySourcePosition == null) {
+ mySourcePosition = ContextUtil.getSourcePosition(DebuggerContextImpl.this);
+ }
+ myContextElement = ContextUtil.getContextElement(mySourcePosition);
+ }
+ });
+ }
+ }
+
+ public void setPositionCache(SourcePosition position) {
+ LOG.assertTrue(!myInitialized, "Debugger context is initialized. Cannot change caches");
+ mySourcePosition = position;
+ }
+
+ public boolean isInitialised() {
+ return myInitialized;
+ }
+
+ public boolean isEvaluationPossible() {
+ return getDebugProcess().getSuspendManager().getPausedContext() != null;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextListener.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextListener.java
new file mode 100644
index 0000000..395a2af
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextListener.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+import java.util.EventListener;
+
+
+public interface DebuggerContextListener extends EventListener{
+ void changeEvent(DebuggerContextImpl newContext, int event);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java
new file mode 100644
index 0000000..a117e74
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.openapi.application.ApplicationManager;
+import org.jetbrains.annotations.NotNull;
+
+public class DebuggerContextUtil {
+ public static void setStackFrame(DebuggerStateManager manager, final StackFrameProxyImpl stackFrame) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ DebuggerContextImpl context = manager.getContext();
+ if(context == null) return;
+
+ DebuggerContextImpl newContext = DebuggerContextImpl.createDebuggerContext(context.getDebuggerSession(), context.getSuspendContext(), stackFrame.threadProxy(), stackFrame);
+
+ manager.setState(newContext, context.getDebuggerSession().getState(), DebuggerSession.EVENT_REFRESH, null);
+ }
+
+ public static void setThread(DebuggerStateManager contextManager, ThreadDescriptorImpl item) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ DebuggerContextImpl context = contextManager.getContext();
+
+ DebuggerContextImpl newContext = DebuggerContextImpl.createDebuggerContext(context.getDebuggerSession(), item.getSuspendContext(), item.getThreadReference(), null);
+
+ contextManager.setState(newContext, context.getDebuggerSession().getState(), DebuggerSession.EVENT_CONTEXT, null);
+ }
+
+ public static DebuggerContextImpl createDebuggerContext(@NotNull DebuggerSession session, SuspendContextImpl suspendContext){
+ return DebuggerContextImpl.createDebuggerContext(session, suspendContext, suspendContext != null ? suspendContext.getThread() : null, null);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerAdapter.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerAdapter.java
new file mode 100644
index 0000000..6fe296e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerAdapter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.impl;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: 1/30/12
+ */
+public class DebuggerManagerAdapter implements DebuggerManagerListener{
+ @Override
+ public void sessionCreated(DebuggerSession session) {
+ }
+
+ @Override
+ public void sessionAttached(DebuggerSession session) {
+ }
+
+ @Override
+ public void sessionDetached(DebuggerSession session) {
+ }
+
+ @Override
+ public void sessionRemoved(DebuggerSession session) {
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java
new file mode 100644
index 0000000..bf78d82
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java
@@ -0,0 +1,594 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.*;
+import com.intellij.debugger.apiAdapters.TransportServiceWrapper;
+import com.intellij.debugger.engine.*;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.GetJPDADialog;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.configurations.ModuleRunProfile;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.colors.EditorColorsListener;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.JdkUtil;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+import java.util.jar.Attributes;
+
+public class DebuggerManagerImpl extends DebuggerManagerEx {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerManagerImpl");
+ private final Project myProject;
+ private final HashMap<ProcessHandler, DebuggerSession> mySessions = new HashMap<ProcessHandler, DebuggerSession>();
+ private final BreakpointManager myBreakpointManager;
+ private final List<NameMapper> myNameMappers = ContainerUtil.createEmptyCOWList();
+ private final List<Function<DebugProcess, PositionManager>> myCustomPositionManagerFactories =
+ new ArrayList<Function<DebugProcess, PositionManager>>();
+
+ private final EventDispatcher<DebuggerManagerListener> myDispatcher = EventDispatcher.create(DebuggerManagerListener.class);
+ private final MyDebuggerStateManager myDebuggerStateManager = new MyDebuggerStateManager();
+
+ private final DebuggerContextListener mySessionListener = new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+
+ final DebuggerSession session = newContext.getDebuggerSession();
+ if (event == DebuggerSession.EVENT_PAUSE && myDebuggerStateManager.myDebuggerSession != session) {
+ // if paused in non-active session; switch current session
+ myDebuggerStateManager.setState(newContext, session.getState(), event, null);
+ return;
+ }
+
+ if (myDebuggerStateManager.myDebuggerSession == session) {
+ myDebuggerStateManager.fireStateChanged(newContext, event);
+ }
+ if (event == DebuggerSession.EVENT_ATTACHED) {
+ myDispatcher.getMulticaster().sessionAttached(session);
+ }
+ else if (event == DebuggerSession.EVENT_DETACHED) {
+ myDispatcher.getMulticaster().sessionDetached(session);
+ }
+ else if (event == DebuggerSession.EVENT_DISPOSE) {
+ dispose(session);
+ if (myDebuggerStateManager.myDebuggerSession == session) {
+ myDebuggerStateManager
+ .setState(DebuggerContextImpl.EMPTY_CONTEXT, DebuggerSession.STATE_DISPOSED, DebuggerSession.EVENT_DISPOSE, null);
+ }
+ }
+ }
+ };
+ @NonNls private static final String DEBUG_KEY_NAME = "idea.xdebug.key";
+
+ public void addClassNameMapper(final NameMapper mapper) {
+ myNameMappers.add(mapper);
+ }
+
+ public void removeClassNameMapper(final NameMapper mapper) {
+ myNameMappers.remove(mapper);
+ }
+
+ public String getVMClassQualifiedName(@NotNull final PsiClass aClass) {
+ for (NameMapper nameMapper : myNameMappers) {
+ final String qName = nameMapper.getQualifiedName(aClass);
+ if (qName != null) {
+ return qName;
+ }
+ }
+ return aClass.getQualifiedName();
+ }
+
+ public void addDebuggerManagerListener(DebuggerManagerListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ public void removeDebuggerManagerListener(DebuggerManagerListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+ public DebuggerManagerImpl(Project project, StartupManager startupManager, final EditorColorsManager colorsManager) {
+ myProject = project;
+ myBreakpointManager = new BreakpointManager(myProject, startupManager, this);
+ final EditorColorsListener myColorsListener = new EditorColorsListener() {
+ public void globalSchemeChange(EditorColorsScheme scheme) {
+ getBreakpointManager().updateBreakpointsUI();
+ }
+ };
+ colorsManager.addEditorColorsListener(myColorsListener);
+ Disposer.register(project, new Disposable() {
+ public void dispose() {
+ colorsManager.removeEditorColorsListener(myColorsListener);
+ }
+ });
+ }
+
+ public DebuggerSession getSession(DebugProcess process) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ for (final DebuggerSession debuggerSession : getSessions()) {
+ if (process == debuggerSession.getProcess()) return debuggerSession;
+ }
+ return null;
+ }
+
+ public Collection<DebuggerSession> getSessions() {
+ synchronized (mySessions) {
+ final Collection<DebuggerSession> values = mySessions.values();
+ return values.isEmpty() ? Collections.<DebuggerSession>emptyList() : new ArrayList<DebuggerSession>(values);
+ }
+ }
+
+ public void disposeComponent() {
+ }
+
+ public void initComponent() {
+ }
+
+ public void projectClosed() {
+ }
+
+ public void projectOpened() {
+ myBreakpointManager.init();
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ myBreakpointManager.readExternal(element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ myBreakpointManager.writeExternal(element);
+ }
+
+
+ public DebuggerSession attachVirtualMachine(Executor executor,
+ ProgramRunner runner,
+ ModuleRunProfile profile,
+ RunProfileState state,
+ RemoteConnection remoteConnection,
+ boolean pollConnection
+ ) throws ExecutionException {
+ return attachVirtualMachine(new DefaultDebugEnvironment(myProject,
+ executor,
+ runner,
+ profile,
+ state,
+ remoteConnection,
+ pollConnection));
+ }
+
+ public DebuggerSession attachVirtualMachine(DebugEnvironment environment) throws ExecutionException {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ final DebugProcessEvents debugProcess = new DebugProcessEvents(myProject);
+ debugProcess.addDebugProcessListener(new DebugProcessAdapter() {
+ public void processAttached(final DebugProcess process) {
+ process.removeDebugProcessListener(this);
+ for (Function<DebugProcess, PositionManager> factory : myCustomPositionManagerFactories) {
+ final PositionManager positionManager = factory.fun(process);
+ if (positionManager != null) {
+ process.appendPositionManager(positionManager);
+ }
+ }
+ for (PositionManagerFactory factory : Extensions.getExtensions(PositionManagerFactory.EP_NAME, myProject)) {
+ final PositionManager manager = factory.createPositionManager(debugProcess);
+ if (manager != null) {
+ process.appendPositionManager(manager);
+ }
+ }
+ }
+
+ public void processDetached(final DebugProcess process, final boolean closedByUser) {
+ debugProcess.removeDebugProcessListener(this);
+ }
+
+ public void attachException(final RunProfileState state,
+ final ExecutionException exception,
+ final RemoteConnection remoteConnection) {
+ debugProcess.removeDebugProcessListener(this);
+ }
+ });
+ final DebuggerSession session = new DebuggerSession(environment.getSessionName(), debugProcess);
+
+ final ExecutionResult executionResult = session.attach(environment);
+ if (executionResult == null) {
+ return null;
+ }
+ session.getContextManager().addListener(mySessionListener);
+ getContextManager()
+ .setState(DebuggerContextUtil.createDebuggerContext(session, session.getContextManager().getContext().getSuspendContext()),
+ session.getState(), DebuggerSession.EVENT_CONTEXT, null);
+
+ final ProcessHandler processHandler = executionResult.getProcessHandler();
+
+ synchronized (mySessions) {
+ mySessions.put(processHandler, session);
+ }
+
+ if (!(processHandler instanceof RemoteDebugProcessHandler)) {
+ // add listener only to non-remote process handler:
+ // on Unix systems destroying process does not cause VMDeathEvent to be generated,
+ // so we need to call debugProcess.stop() explicitly for graceful termination.
+ // RemoteProcessHandler on the other hand will call debugProcess.stop() as a part of destroyProcess() and detachProcess() implementation,
+ // so we shouldn't add the listener to avoid calling stop() twice
+ processHandler.addProcessListener(new ProcessAdapter() {
+ public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
+ final DebugProcessImpl debugProcess = getDebugProcess(event.getProcessHandler());
+ if (debugProcess != null) {
+ // if current thread is a "debugger manager thread", stop will execute synchronously
+ debugProcess.stop(willBeDestroyed);
+
+ // wait at most 10 seconds: the problem is that debugProcess.stop() can hang if there are troubles in the debuggee
+ // if processWillTerminate() is called from AWT thread debugProcess.waitFor() will block it and the whole app will hang
+ if (!DebuggerManagerThreadImpl.isManagerThread()) {
+ debugProcess.waitFor(10000);
+ }
+ }
+ }
+ });
+ }
+ myDispatcher.getMulticaster().sessionCreated(session);
+ return session;
+ }
+
+
+ public DebugProcessImpl getDebugProcess(final ProcessHandler processHandler) {
+ synchronized (mySessions) {
+ DebuggerSession session = mySessions.get(processHandler);
+ return session != null ? session.getProcess() : null;
+ }
+ }
+
+ @Nullable
+ public DebuggerSession getDebugSession(final ProcessHandler processHandler) {
+ synchronized (mySessions) {
+ return mySessions.get(processHandler);
+ }
+ }
+
+ public void addDebugProcessListener(final ProcessHandler processHandler, final DebugProcessListener listener) {
+ DebugProcessImpl debugProcess = getDebugProcess(processHandler);
+ if (debugProcess != null) {
+ debugProcess.addDebugProcessListener(listener);
+ }
+ else {
+ processHandler.addProcessListener(new ProcessAdapter() {
+ public void startNotified(ProcessEvent event) {
+ DebugProcessImpl debugProcess = getDebugProcess(processHandler);
+ if (debugProcess != null) {
+ debugProcess.addDebugProcessListener(listener);
+ }
+ processHandler.removeProcessListener(this);
+ }
+ });
+ }
+ }
+
+ public void removeDebugProcessListener(final ProcessHandler processHandler, final DebugProcessListener listener) {
+ DebugProcessImpl debugProcess = getDebugProcess(processHandler);
+ if (debugProcess != null) {
+ debugProcess.removeDebugProcessListener(listener);
+ }
+ else {
+ processHandler.addProcessListener(new ProcessAdapter() {
+ public void startNotified(ProcessEvent event) {
+ DebugProcessImpl debugProcess = getDebugProcess(processHandler);
+ if (debugProcess != null) {
+ debugProcess.removeDebugProcessListener(listener);
+ }
+ processHandler.removeProcessListener(this);
+ }
+ });
+ }
+ }
+
+ public boolean isDebuggerManagerThread() {
+ return DebuggerManagerThreadImpl.isManagerThread();
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "DebuggerManager";
+ }
+
+ public BreakpointManager getBreakpointManager() {
+ return myBreakpointManager;
+ }
+
+ public DebuggerContextImpl getContext() {
+ return getContextManager().getContext();
+ }
+
+ public DebuggerStateManager getContextManager() {
+ return myDebuggerStateManager;
+ }
+
+ public void registerPositionManagerFactory(final Function<DebugProcess, PositionManager> factory) {
+ myCustomPositionManagerFactories.add(factory);
+ }
+
+ public void unregisterPositionManagerFactory(final Function<DebugProcess, PositionManager> factory) {
+ myCustomPositionManagerFactories.remove(factory);
+ }
+
+ private static boolean hasWhitespace(String string) {
+ int length = string.length();
+ for (int i = 0; i < length; i++) {
+ if (Character.isWhitespace(string.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* Remoting */
+ private static void checkTargetJPDAInstalled(JavaParameters parameters) throws ExecutionException {
+ final Sdk jdk = parameters.getJdk();
+ if (jdk == null) {
+ throw new ExecutionException(DebuggerBundle.message("error.jdk.not.specified"));
+ }
+ final JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk);
+ String versionString = jdk.getVersionString();
+ if (version == JavaSdkVersion.JDK_1_0 || version == JavaSdkVersion.JDK_1_1) {
+ throw new ExecutionException(DebuggerBundle.message("error.unsupported.jdk.version", versionString));
+ }
+ if (SystemInfo.isWindows && version == JavaSdkVersion.JDK_1_2) {
+ final VirtualFile homeDirectory = jdk.getHomeDirectory();
+ if (homeDirectory == null || !homeDirectory.isValid()) {
+ throw new ExecutionException(DebuggerBundle.message("error.invalid.jdk.home", versionString));
+ }
+ //noinspection HardCodedStringLiteral
+ File dllFile = new File(
+ homeDirectory.getPath().replace('/', File.separatorChar) + File.separator + "bin" + File.separator + "jdwp.dll"
+ );
+ if (!dllFile.exists()) {
+ GetJPDADialog dialog = new GetJPDADialog();
+ dialog.show();
+ throw new ExecutionException(DebuggerBundle.message("error.debug.libraries.missing"));
+ }
+ }
+ }
+
+ /**
+ * for Target JDKs versions 1.2.x - 1.3.0 the Classic VM should be used for debugging
+ */
+ private static boolean shouldForceClassicVM(Sdk jdk) {
+ if (SystemInfo.isMac) {
+ return false;
+ }
+ if (jdk == null) return false;
+
+ String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION);
+ if (version != null) {
+ if (version.compareTo("1.4") >= 0) {
+ return false;
+ }
+ if (version.startsWith("1.2") && SystemInfo.isWindows) {
+ return true;
+ }
+ version += ".0";
+ if (version.startsWith("1.3.0") && SystemInfo.isWindows) {
+ return true;
+ }
+ if ((version.startsWith("1.3.1_07") || version.startsWith("1.3.1_08")) && SystemInfo.isWindows) {
+ return false; // fixes bug for these JDKs that it cannot start with -classic option
+ }
+ }
+
+ return DebuggerSettings.getInstance().FORCE_CLASSIC_VM;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public static RemoteConnection createDebugParameters(final JavaParameters parameters,
+ final boolean debuggerInServerMode,
+ int transport, final String debugPort,
+ boolean checkValidity)
+ throws ExecutionException {
+ if (checkValidity) {
+ checkTargetJPDAInstalled(parameters);
+ }
+
+ final boolean useSockets = transport == DebuggerSettings.SOCKET_TRANSPORT;
+
+ String address = "";
+ if (debugPort == null || "".equals(debugPort)) {
+ try {
+ address = DebuggerUtils.getInstance().findAvailableDebugAddress(useSockets);
+ }
+ catch (ExecutionException e) {
+ if (checkValidity) {
+ throw e;
+ }
+ }
+ }
+ else {
+ address = debugPort;
+ }
+
+ final TransportServiceWrapper transportService = TransportServiceWrapper.getTransportService(useSockets);
+ final String debugAddress = debuggerInServerMode && useSockets ? "127.0.0.1:" + address : address;
+ String debuggeeRunProperties = "transport=" + transportService.transportId() + ",address=" + debugAddress;
+ if (debuggerInServerMode) {
+ debuggeeRunProperties += ",suspend=y,server=n";
+ }
+ else {
+ debuggeeRunProperties += ",suspend=n,server=y";
+ }
+
+ if (hasWhitespace(debuggeeRunProperties)) {
+ debuggeeRunProperties = "\"" + debuggeeRunProperties + "\"";
+ }
+ final String _debuggeeRunProperties = debuggeeRunProperties;
+
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void run() {
+ JavaSdkUtil.addRtJar(parameters.getClassPath());
+
+ final Sdk jdk = parameters.getJdk();
+ final boolean forceClassicVM = shouldForceClassicVM(jdk);
+ final boolean forceNoJIT = shouldForceNoJIT(jdk);
+ final String debugKey = System.getProperty(DEBUG_KEY_NAME, "-Xdebug");
+ final boolean needDebugKey = shouldAddXdebugKey(jdk) || !"-Xdebug".equals(debugKey) /*the key is non-standard*/;
+
+ if (forceClassicVM || forceNoJIT || needDebugKey || !isJVMTIAvailable(jdk)) {
+ parameters.getVMParametersList().replaceOrPrepend("-Xrunjdwp:", "-Xrunjdwp:" + _debuggeeRunProperties);
+ }
+ else {
+ // use newer JVMTI if available
+ parameters.getVMParametersList().replaceOrPrepend("-Xrunjdwp:", "");
+ parameters.getVMParametersList().replaceOrPrepend("-agentlib:jdwp=", "-agentlib:jdwp=" + _debuggeeRunProperties);
+ }
+
+ if (forceNoJIT) {
+ parameters.getVMParametersList().replaceOrPrepend("-Djava.compiler=", "-Djava.compiler=NONE");
+ parameters.getVMParametersList().replaceOrPrepend("-Xnoagent", "-Xnoagent");
+ }
+
+ if (needDebugKey) {
+ parameters.getVMParametersList().replaceOrPrepend(debugKey, debugKey);
+ }
+ else {
+ // deliberately skip outdated parameter because it can disable full-speed debugging for some jdk builds
+ // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6272174
+ parameters.getVMParametersList().replaceOrPrepend("-Xdebug", "");
+ }
+
+ parameters.getVMParametersList().replaceOrPrepend("-classic", forceClassicVM ? "-classic" : "");
+ }
+ });
+
+ return new RemoteConnection(useSockets, "127.0.0.1", address, debuggerInServerMode);
+ }
+
+ private static boolean shouldForceNoJIT(Sdk jdk) {
+ if (DebuggerSettings.getInstance().DISABLE_JIT) {
+ return true;
+ }
+ if (jdk != null) {
+ final String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION);
+ if (version != null && (version.startsWith("1.2") || version.startsWith("1.3"))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean shouldAddXdebugKey(Sdk jdk) {
+ if (jdk == null) {
+ return true; // conservative choice
+ }
+ if (DebuggerSettings.getInstance().DISABLE_JIT) {
+ return true;
+ }
+
+ //if (ApplicationManager.getApplication().isUnitTestMode()) {
+ // need this in unit tests to avoid false alarms when comparing actual output with expected output
+ //return true;
+ //}
+
+ final String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION);
+ return version == null ||
+ //version.startsWith("1.5") ||
+ version.startsWith("1.4") ||
+ version.startsWith("1.3") ||
+ version.startsWith("1.2") ||
+ version.startsWith("1.1") ||
+ version.startsWith("1.0");
+ }
+
+ private static boolean isJVMTIAvailable(Sdk jdk) {
+ if (jdk == null) {
+ return false; // conservative choice
+ }
+
+ final String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION);
+ if (version == null) {
+ return false;
+ }
+ return !(version.startsWith("1.4") ||
+ version.startsWith("1.3") ||
+ version.startsWith("1.2") ||
+ version.startsWith("1.1") ||
+ version.startsWith("1.0"));
+ }
+
+ public static RemoteConnection createDebugParameters(final JavaParameters parameters,
+ GenericDebuggerRunnerSettings settings,
+ boolean checkValidity)
+ throws ExecutionException {
+ return createDebugParameters(parameters, settings.LOCAL, settings.getTransport(), settings.DEBUG_PORT, checkValidity);
+ }
+
+ private static class MyDebuggerStateManager extends DebuggerStateManager {
+ private DebuggerSession myDebuggerSession;
+
+ public DebuggerContextImpl getContext() {
+ return myDebuggerSession == null ? DebuggerContextImpl.EMPTY_CONTEXT : myDebuggerSession.getContextManager().getContext();
+ }
+
+ public void setState(final DebuggerContextImpl context, int state, int event, String description) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ myDebuggerSession = context.getDebuggerSession();
+ if (myDebuggerSession != null) {
+ myDebuggerSession.getContextManager().setState(context, state, event, description);
+ }
+ else {
+ fireStateChanged(context, event);
+ }
+ }
+ }
+
+ private void dispose(DebuggerSession session) {
+ ProcessHandler processHandler = session.getProcess().getExecutionResult().getProcessHandler();
+
+ synchronized (mySessions) {
+ DebuggerSession removed = mySessions.remove(processHandler);
+ LOG.assertTrue(removed != null);
+ myDispatcher.getMulticaster().sessionRemoved(session);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerListener.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerListener.java
new file mode 100644
index 0000000..ff4261f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerListener.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import java.util.EventListener;
+
+public interface DebuggerManagerListener extends EventListener{
+ void sessionCreated(DebuggerSession session);
+ void sessionAttached(DebuggerSession session);
+ void sessionDetached(DebuggerSession session);
+ void sessionRemoved(DebuggerSession session);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java
new file mode 100644
index 0000000..e3773a3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java
@@ -0,0 +1,628 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.*;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.*;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationListener;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter;
+import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.ModuleRunProfile;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RemoteState;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiCompiledElement;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.unscramble.ThreadState;
+import com.intellij.util.Alarm;
+import com.intellij.xdebugger.AbstractDebuggerSession;
+import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.event.Event;
+import com.sun.jdi.request.EventRequest;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DebuggerSession implements AbstractDebuggerSession {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerSession");
+ // flags
+ private final MyDebuggerStateManager myContextManager;
+
+ public static final int STATE_STOPPED = 0;
+ public static final int STATE_RUNNING = 1;
+ public static final int STATE_WAITING_ATTACH = 2;
+ public static final int STATE_PAUSED = 3;
+ public static final int STATE_WAIT_EVALUATION = 5;
+ public static final int STATE_DISPOSED = 6;
+
+ public static final int EVENT_ATTACHED = 0;
+ public static final int EVENT_DETACHED = 1;
+ public static final int EVENT_RESUME = 4;
+ public static final int EVENT_STEP = 5;
+ public static final int EVENT_PAUSE = 6;
+ public static final int EVENT_REFRESH = 7;
+ public static final int EVENT_CONTEXT = 8;
+ public static final int EVENT_START_WAIT_ATTACH = 9;
+ public static final int EVENT_DISPOSE = 10;
+ public static final int EVENT_REFRESH_VIEWS_ONLY = 11;
+ public static final int EVENT_THREADS_REFRESH = 12;
+
+ private volatile boolean myIsEvaluating;
+ private volatile int myIgnoreFiltersFrameCountThreshold = 0;
+
+ private DebuggerSessionState myState = null;
+
+ private final String mySessionName;
+ private final DebugProcessImpl myDebugProcess;
+ private @NotNull GlobalSearchScope mySearchScope;
+
+ private final DebuggerContextImpl SESSION_EMPTY_CONTEXT;
+ //Thread, user is currently stepping through
+ private final Set<ThreadReferenceProxyImpl> mySteppingThroughThreads = new HashSet<ThreadReferenceProxyImpl>();
+ protected final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+
+ private boolean myModifiedClassesScanRequired = false;
+
+ public boolean isSteppingThrough(ThreadReferenceProxyImpl threadProxy) {
+ return mySteppingThroughThreads.contains(threadProxy);
+ }
+
+ @NotNull
+ public GlobalSearchScope getSearchScope() {
+ LOG.assertTrue(mySearchScope != null, "Accessing Session's search scope before its initialization");
+ return mySearchScope;
+ }
+
+ public boolean isModifiedClassesScanRequired() {
+ return myModifiedClassesScanRequired;
+ }
+
+ public void setModifiedClassesScanRequired(boolean modifiedClassesScanRequired) {
+ myModifiedClassesScanRequired = modifiedClassesScanRequired;
+ }
+
+ private class MyDebuggerStateManager extends DebuggerStateManager {
+ private DebuggerContextImpl myDebuggerContext;
+
+ MyDebuggerStateManager() {
+ myDebuggerContext = SESSION_EMPTY_CONTEXT;
+ }
+
+ public DebuggerContextImpl getContext() {
+ return myDebuggerContext;
+ }
+
+ /**
+ * actually state changes not in the same sequence as you call setState
+ * the 'resuming' setState with context.getSuspendContext() == null may be set prior to
+ * the setState for the context with context.getSuspendContext()
+ *
+ * in this case we assume that the latter setState is ignored
+ * since the thread was resumed
+ */
+ public void setState(final DebuggerContextImpl context, final int state, final int event, final String description) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ LOG.assertTrue(context.getDebuggerSession() == DebuggerSession.this || context.getDebuggerSession() == null);
+ final Runnable setStateRunnable = new Runnable() {
+ public void run() {
+ LOG.assertTrue(myDebuggerContext.isInitialised());
+ myDebuggerContext = context;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("DebuggerSession state = " + state + ", event = " + event);
+ }
+
+ myIsEvaluating = false;
+
+ myState = new DebuggerSessionState(state, description);
+ fireStateChanged(context, event);
+ }
+ };
+
+ if(context.getSuspendContext() == null) {
+ setStateRunnable.run();
+ }
+ else {
+ getProcess().getManagerThread().schedule(new SuspendContextCommandImpl(context.getSuspendContext()) {
+ public void contextAction() throws Exception {
+ context.initCaches();
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), setStateRunnable);
+ }
+ });
+ }
+ }
+ }
+
+ protected DebuggerSession(String sessionName, final DebugProcessImpl debugProcess) {
+ mySessionName = sessionName;
+ myDebugProcess = debugProcess;
+ SESSION_EMPTY_CONTEXT = DebuggerContextImpl.createDebuggerContext(this, null, null, null);
+ myContextManager = new MyDebuggerStateManager();
+ myState = new DebuggerSessionState(STATE_STOPPED, null);
+ myDebugProcess.addDebugProcessListener(new MyDebugProcessListener(debugProcess));
+ myDebugProcess.addEvaluationListener(new MyEvaluationListener());
+ ValueLookupManager.getInstance(getProject()).startListening();
+ }
+
+ public DebuggerStateManager getContextManager() {
+ return myContextManager;
+ }
+
+ public Project getProject() {
+ return getProcess().getProject();
+ }
+
+ public String getSessionName() {
+ return mySessionName;
+ }
+
+ public DebugProcessImpl getProcess() {
+ return myDebugProcess;
+ }
+
+ private static class DebuggerSessionState {
+ final int myState;
+ final String myDescription;
+
+ public DebuggerSessionState(int state, String description) {
+ myState = state;
+ myDescription = description;
+ }
+ }
+
+ public int getState() {
+ return myState.myState;
+ }
+
+ public String getStateDescription() {
+ if (myState.myDescription != null) {
+ return myState.myDescription;
+ }
+
+ switch (myState.myState) {
+ case STATE_STOPPED:
+ return DebuggerBundle.message("status.debug.stopped");
+ case STATE_RUNNING:
+ return DebuggerBundle.message("status.app.running");
+ case STATE_WAITING_ATTACH:
+ RemoteConnection connection = getProcess().getConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
+ final String transportName = DebuggerBundle.getTransportName(connection);
+ return connection.isServerMode() ? DebuggerBundle.message("status.listening", addressDisplayName, transportName) : DebuggerBundle.message("status.connecting", addressDisplayName, transportName);
+ case STATE_PAUSED:
+ return DebuggerBundle.message("status.paused");
+ case STATE_WAIT_EVALUATION:
+ return DebuggerBundle.message("status.waiting.evaluation.result");
+ case STATE_DISPOSED:
+ return DebuggerBundle.message("status.debug.stopped");
+ }
+ return myState.myDescription;
+ }
+
+ /* Stepping */
+ private void resumeAction(final DebugProcessImpl.ResumeCommand command, int event) {
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAIT_EVALUATION, event, null);
+ myDebugProcess.getManagerThread().schedule(command);
+ }
+
+ public void stepOut() {
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ final DebugProcessImpl.ResumeCommand cmd = myDebugProcess.createStepOutCommand(suspendContext);
+ mySteppingThroughThreads.add(cmd.getContextThread());
+ resumeAction(cmd, EVENT_STEP);
+ }
+
+ public void stepOver(boolean ignoreBreakpoints) {
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ final DebugProcessImpl.ResumeCommand cmd = myDebugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints);
+ mySteppingThroughThreads.add(cmd.getContextThread());
+ resumeAction(cmd, EVENT_STEP);
+ }
+
+ public void stepInto(final boolean ignoreFilters, final @Nullable RequestHint.SmartStepFilter smartStepFilter) {
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ final DebugProcessImpl.ResumeCommand cmd = myDebugProcess.createStepIntoCommand(suspendContext, ignoreFilters, smartStepFilter);
+ mySteppingThroughThreads.add(cmd.getContextThread());
+ resumeAction(cmd, EVENT_STEP);
+ }
+
+ public void runToCursor(Document document, int line, final boolean ignoreBreakpoints) {
+ try {
+ DebugProcessImpl.ResumeCommand runToCursorCommand = myDebugProcess.createRunToCursorCommand(getSuspendContext(), document, line, ignoreBreakpoints);
+ mySteppingThroughThreads.add(runToCursorCommand.getContextThread());
+ resumeAction(runToCursorCommand, EVENT_STEP);
+ }
+ catch (EvaluateException e) {
+ Messages.showErrorDialog(e.getMessage(), ActionsBundle.actionText(DebuggerActions.RUN_TO_CURSOR));
+ }
+ }
+
+
+ public void resume() {
+ final SuspendContextImpl suspendContext = getSuspendContext();
+ if(suspendContext != null) {
+ mySteppingThroughThreads.clear();
+ resetIgnoreStepFiltersFlag();
+ resumeAction(myDebugProcess.createResumeCommand(suspendContext), EVENT_RESUME);
+ }
+ }
+
+ private void resetIgnoreStepFiltersFlag() {
+ myIgnoreFiltersFrameCountThreshold = 0;
+ }
+
+ public void setIgnoreStepFiltersFlag(int currentStackFrameCount) {
+ myIgnoreFiltersFrameCountThreshold = currentStackFrameCount;
+ }
+
+ public boolean shouldIgnoreSteppingFilters() {
+ return myIgnoreFiltersFrameCountThreshold > 0;
+ }
+
+ public void pause() {
+ myDebugProcess.getManagerThread().schedule(myDebugProcess.createPauseCommand());
+ }
+
+ /*Presentation*/
+
+ public void showExecutionPoint() {
+ getContextManager().setState(DebuggerContextUtil.createDebuggerContext(this, getSuspendContext()), STATE_PAUSED, EVENT_REFRESH, null);
+ }
+
+ public void refresh(final boolean refreshViewsOnly) {
+ final int state = getState();
+ DebuggerContextImpl context = myContextManager.getContext();
+ DebuggerContextImpl newContext = DebuggerContextImpl.createDebuggerContext(this, context.getSuspendContext(), context.getThreadProxy(), context.getFrameProxy());
+ myContextManager.setState(newContext, state, refreshViewsOnly? EVENT_REFRESH_VIEWS_ONLY : EVENT_REFRESH, null);
+ }
+
+ public void dispose() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ getProcess().dispose();
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_DISPOSED, EVENT_DISPOSE, null);
+ Disposer.dispose(myUpdateAlarm);
+ }
+
+ // ManagerCommands
+ public boolean isStopped() {
+ return getState() == STATE_STOPPED;
+ }
+
+ public boolean isAttached() {
+ return !isStopped() && getState() != STATE_WAITING_ATTACH;
+ }
+
+ public boolean isPaused() {
+ return getState() == STATE_PAUSED;
+ }
+
+ public boolean isConnecting() {
+ return getState() == STATE_WAITING_ATTACH;
+ }
+
+ public boolean isEvaluating() {
+ return myIsEvaluating;
+ }
+
+ public boolean isRunning() {
+ return getState() == STATE_RUNNING && !getProcess().getExecutionResult().getProcessHandler().isProcessTerminated();
+ }
+
+ private SuspendContextImpl getSuspendContext() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ return getContextManager().getContext().getSuspendContext();
+ }
+
+ @Nullable
+ protected ExecutionResult attach(@NotNull final Executor executor,
+ @NotNull final ProgramRunner runner,
+ final ModuleRunProfile profile,
+ final RunProfileState state,
+ final RemoteConnection remoteConnection,
+ final boolean pollConnection) throws ExecutionException {
+ return attach(new DefaultDebugEnvironment(myDebugProcess.getProject(),
+ executor,
+ runner,
+ profile,
+ state,
+ remoteConnection,
+ pollConnection));
+ }
+
+ @Nullable
+ protected ExecutionResult attach(DebugEnvironment environment) throws ExecutionException {
+ RemoteConnection remoteConnection = environment.getRemoteConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(remoteConnection);
+ final String transportName = DebuggerBundle.getTransportName(remoteConnection);
+ mySearchScope = environment.getSearchScope();
+ final ExecutionResult executionResult = myDebugProcess.attachVirtualMachine(environment, this);
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAITING_ATTACH, EVENT_START_WAIT_ATTACH, DebuggerBundle.message("status.waiting.attach", addressDisplayName, transportName));
+ return executionResult;
+ }
+
+ private class MyDebugProcessListener extends DebugProcessAdapterImpl {
+ private final DebugProcessImpl myDebugProcess;
+
+ public MyDebugProcessListener(final DebugProcessImpl debugProcess) {
+ myDebugProcess = debugProcess;
+ }
+
+ //executed in manager thread
+ public void connectorIsReady() {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ RemoteConnection connection = myDebugProcess.getConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
+ final String transportName = DebuggerBundle.getTransportName(connection);
+ final String connectionStatusMessage = connection.isServerMode() ? DebuggerBundle.message("status.listening", addressDisplayName, transportName) : DebuggerBundle.message("status.connecting", addressDisplayName, transportName);
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, DebuggerSession.STATE_WAITING_ATTACH, DebuggerSession.EVENT_START_WAIT_ATTACH, connectionStatusMessage);
+ }
+ });
+ }
+
+ public void paused(final SuspendContextImpl suspendContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("paused");
+ }
+
+ ThreadReferenceProxyImpl currentThread = suspendContext.getThread();
+ final StackFrameContext positionContext;
+
+ if (currentThread == null) {
+ //Pause pressed
+ LOG.assertTrue(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL);
+ SuspendContextImpl oldContext = getProcess().getSuspendManager().getPausedContext();
+
+ if (oldContext != null) {
+ currentThread = oldContext.getThread();
+ }
+
+ if(currentThread == null) {
+ final Collection<ThreadReferenceProxyImpl> allThreads = getProcess().getVirtualMachineProxy().allThreads();
+ // heuristics: try to pre-select EventDispatchThread
+ for (final ThreadReferenceProxyImpl thread : allThreads) {
+ if (ThreadState.isEDT(thread.name())) {
+ currentThread = thread;
+ break;
+ }
+ }
+ if (currentThread == null) {
+ // heuristics: display the first thread with RUNNABLE status
+ for (final ThreadReferenceProxyImpl thread : allThreads) {
+ currentThread = thread;
+ if (currentThread.status() == ThreadReference.THREAD_STATUS_RUNNING) {
+ break;
+ }
+ }
+ }
+ }
+
+ StackFrameProxyImpl proxy = null;
+ if (currentThread != null) {
+ try {
+ while (!currentThread.isSuspended()) {
+ // wait until thread is considered suspended. Querying data from a thread immediately after VM.suspend()
+ // may result in IncompatibleThreadStateException, most likely some time after suspend() VM erroneously thinks that thread is still running
+ try {
+ Thread.sleep(10);
+ }
+ catch (InterruptedException ignored) {}
+ }
+ proxy = (currentThread.frameCount() > 0) ? currentThread.frame(0) : null;
+ }
+ catch (ObjectCollectedException e) {
+ proxy = null;
+ }
+ catch (EvaluateException e) {
+ proxy = null;
+ LOG.error(e);
+ }
+ }
+ positionContext = new SimpleStackFrameContext(proxy, myDebugProcess);
+ }
+ else {
+ positionContext = suspendContext;
+ }
+
+ if (currentThread != null) {
+ try {
+ final int frameCount = currentThread.frameCount();
+ if (frameCount == 0 || (frameCount < myIgnoreFiltersFrameCountThreshold)) {
+ resetIgnoreStepFiltersFlag();
+ }
+ }
+ catch (EvaluateException e) {
+ LOG.info(e);
+ resetIgnoreStepFiltersFlag();
+ }
+ }
+
+ SourcePosition position = PsiDocumentManager.getInstance(getProject()).commitAndRunReadAction(new Computable<SourcePosition>() {
+ public @Nullable SourcePosition compute() {
+ return ContextUtil.getSourcePosition(positionContext);
+ }
+ });
+
+ if (position != null) {
+ final List<Pair<Breakpoint, Event>> eventDescriptors = DebuggerUtilsEx.getEventDescriptors(suspendContext);
+ final RequestManagerImpl requestsManager = suspendContext.getDebugProcess().getRequestsManager();
+ final PsiFile foundFile = position.getFile();
+ final boolean sourceMissing = foundFile == null || foundFile instanceof PsiCompiledElement;
+ for (Pair<Breakpoint, Event> eventDescriptor : eventDescriptors) {
+ Breakpoint breakpoint = eventDescriptor.getFirst();
+ if (breakpoint instanceof LineBreakpoint) {
+ final SourcePosition breakpointPosition = ((BreakpointWithHighlighter)breakpoint).getSourcePosition();
+ if (breakpointPosition == null || (!sourceMissing && breakpointPosition.getLine() != position.getLine())) {
+ requestsManager.deleteRequest(breakpoint);
+ requestsManager.setInvalid(breakpoint, DebuggerBundle.message("error.invalid.breakpoint.source.changed"));
+ breakpoint.updateUI();
+ }
+ else if (sourceMissing) {
+ // adjust position to be position of the breakpoint in order to show the real originator of the event
+ position = breakpointPosition;
+ String className;
+ try {
+ className = positionContext.getFrameProxy().location().declaringType().name();
+ }
+ catch (EvaluateException e) {
+ className = "";
+ }
+ requestsManager.setInvalid(breakpoint, DebuggerBundle.message("error.invalid.breakpoint.source.not.found", className));
+ breakpoint.updateUI();
+ }
+ }
+ }
+ }
+
+ final DebuggerContextImpl debuggerContext = DebuggerContextImpl.createDebuggerContext(DebuggerSession.this, suspendContext, currentThread, null);
+ debuggerContext.setPositionCache(position);
+
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ getContextManager().setState(debuggerContext, STATE_PAUSED, EVENT_PAUSE, null);
+ }
+ });
+ }
+
+ public void resumed(final SuspendContextImpl suspendContext) {
+ final SuspendContextImpl currentContext = getProcess().getSuspendManager().getPausedContext();
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ if (currentContext != null) {
+ getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, currentContext), STATE_PAUSED, EVENT_CONTEXT, null);
+ }
+ else {
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_RUNNING, EVENT_CONTEXT, null);
+ }
+ }
+ });
+ }
+
+ public void processAttached(final DebugProcessImpl process) {
+ final RemoteConnection connection = getProcess().getConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
+ final String transportName = DebuggerBundle.getTransportName(connection);
+ final String message = DebuggerBundle.message("status.connected", addressDisplayName, transportName);
+
+ process.getExecutionResult().getProcessHandler().notifyTextAvailable(message + "\n", ProcessOutputTypes.SYSTEM);
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_RUNNING, EVENT_ATTACHED, message);
+ }
+ });
+ }
+
+ public void attachException(final RunProfileState state, final ExecutionException exception, final RemoteConnection remoteConnection) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ String message = "";
+ if (state instanceof RemoteState) {
+ message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
+ }
+ message += exception.getMessage();
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_STOPPED, EVENT_DETACHED, message);
+ }
+ });
+ }
+
+ public void processDetached(final DebugProcessImpl debugProcess, boolean closedByUser) {
+ if (!closedByUser) {
+ ExecutionResult executionResult = debugProcess.getExecutionResult();
+ if(executionResult != null) {
+ final RemoteConnection connection = getProcess().getConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
+ final String transportName = DebuggerBundle.getTransportName(connection);
+ executionResult.getProcessHandler().notifyTextAvailable(DebuggerBundle.message("status.disconnected", addressDisplayName, transportName) + "\n", ProcessOutputTypes.SYSTEM);
+ }
+ }
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ final RemoteConnection connection = getProcess().getConnection();
+ final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
+ final String transportName = DebuggerBundle.getTransportName(connection);
+ getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_STOPPED, EVENT_DETACHED, DebuggerBundle.message("status.disconnected", addressDisplayName, transportName));
+ }
+ });
+ mySteppingThroughThreads.clear();
+ }
+
+ public void threadStarted(DebugProcess proc, ThreadReference thread) {
+ notifyThreadsRefresh();
+ }
+
+ public void threadStopped(DebugProcess proc, ThreadReference thread) {
+ notifyThreadsRefresh();
+ }
+
+ private void notifyThreadsRefresh() {
+ if (!myUpdateAlarm.isDisposed()) {
+ myUpdateAlarm.cancelAllRequests();
+ myUpdateAlarm.addRequest(new Runnable() {
+ public void run() {
+ final DebuggerStateManager contextManager = getContextManager();
+ contextManager.fireStateChanged(contextManager.getContext(), EVENT_THREADS_REFRESH);
+ }
+ }, 100, ModalityState.NON_MODAL);
+ }
+ }
+ }
+
+ private class MyEvaluationListener implements EvaluationListener {
+ public void evaluationStarted(SuspendContextImpl context) {
+ myIsEvaluating = true;
+ }
+
+ public void evaluationFinished(final SuspendContextImpl context) {
+ myIsEvaluating = false;
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ if (context != getSuspendContext()) {
+ getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, context), STATE_PAUSED, EVENT_REFRESH, null);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerStateManager.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerStateManager.java
new file mode 100644
index 0000000..e07ab9b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerStateManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.util.EventDispatcher;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jun 4, 2003
+ * Time: 12:45:56 PM
+ * To change this template use Options | File Templates.
+ */
+public abstract class DebuggerStateManager {
+ private final EventDispatcher<DebuggerContextListener> myEventDispatcher = EventDispatcher.create(DebuggerContextListener.class);
+
+ public abstract DebuggerContextImpl getContext();
+
+ public abstract void setState(DebuggerContextImpl context, int state, int event, String description);
+
+ //we allow add listeners inside DebuggerContextListener.changeEvent
+ public void addListener(DebuggerContextListener listener){
+ myEventDispatcher.addListener(listener);
+ }
+
+ //we allow remove listeners inside DebuggerContextListener.changeEvent
+ public void removeListener(DebuggerContextListener listener){
+ myEventDispatcher.removeListener(listener);
+ }
+
+ protected void fireStateChanged(DebuggerContextImpl newContext, int event) {
+ myEventDispatcher.getMulticaster().changeEvent(newContext, event);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerTask.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerTask.java
new file mode 100644
index 0000000..81639f2
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerTask.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+/**
+ * @author lex
+ */
+public interface DebuggerTask extends PrioritizedTask {
+ void release();
+
+ void hold();
+
+ void waitFor();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerTaskImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerTaskImpl.java
new file mode 100644
index 0000000..6e75e66
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerTaskImpl.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+/**
+ * @author lex
+ */
+public abstract class DebuggerTaskImpl implements DebuggerTask {
+ private int myHolds = 0;
+
+ public synchronized final void release() {
+ if (myHolds > 0) {
+ if (--myHolds == 0) {
+ notifyAll();
+ }
+ }
+ }
+
+ public synchronized final void hold() {
+ myHolds++;
+ }
+
+ public synchronized final void waitFor() {
+ while (myHolds > 0) {
+ try {
+ wait();
+ }
+ catch (InterruptedException ignored) {
+ }
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java
new file mode 100644
index 0000000..88c3f50
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java
@@ -0,0 +1,591 @@
+/*
+ * Copyright 2000-2011 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 DebuggerUtilsEx
+ * @author Jeka
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.requests.Requestor;
+import com.intellij.debugger.ui.CompletionEditor;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.psi.*;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.sun.jdi.*;
+import com.sun.jdi.event.Event;
+import org.jdom.Attribute;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.regex.PatternSyntaxException;
+
+public abstract class DebuggerUtilsEx extends DebuggerUtils {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerUtilsEx");
+
+ private static final int MAX_LABEL_SIZE = 255;
+
+ /**
+ * @param context
+ * @return all CodeFragmentFactoryProviders that provide code fragment factories sutable in the context given
+ */
+ public static List<CodeFragmentFactory> getCodeFragmentFactories(@Nullable PsiElement context) {
+ final DefaultCodeFragmentFactory defaultFactry = DefaultCodeFragmentFactory.getInstance();
+ final CodeFragmentFactory[] providers = ApplicationManager.getApplication().getExtensions(CodeFragmentFactory.EXTENSION_POINT_NAME);
+ final List<CodeFragmentFactory> suitableFactories = new ArrayList<CodeFragmentFactory>(providers.length);
+ if (providers.length > 0) {
+ for (CodeFragmentFactory factory : providers) {
+ if (factory != defaultFactry && factory.isContextAccepted(context)) {
+ suitableFactories.add(factory);
+ }
+ }
+ }
+ suitableFactories.add(defaultFactry); // let default factory be the last one
+ return suitableFactories;
+ }
+
+
+ public static PsiMethod findPsiMethod(PsiFile file, int offset) {
+ PsiElement element = null;
+
+ while(offset >= 0) {
+ element = file.findElementAt(offset);
+ if(element != null) break;
+ offset --;
+ }
+
+ for (; element != null; element = element.getParent()) {
+ if (element instanceof PsiClass) return null;
+ if (element instanceof PsiMethod) return (PsiMethod)element;
+ }
+ return null;
+ }
+
+
+ public static boolean isAssignableFrom(final String baseQualifiedName, ReferenceType checkedType) {
+ if (CommonClassNames.JAVA_LANG_OBJECT.equals(baseQualifiedName)) {
+ return true;
+ }
+ return getSuperClass(baseQualifiedName, checkedType) != null;
+ }
+
+ public static ReferenceType getSuperClass(final String baseQualifiedName, ReferenceType checkedType) {
+ if (baseQualifiedName.equals(checkedType.name())) {
+ return checkedType;
+ }
+
+ if (checkedType instanceof ClassType) {
+ ClassType classType = (ClassType)checkedType;
+ ClassType superClassType = classType.superclass();
+ if (superClassType != null) {
+ ReferenceType superClass = getSuperClass(baseQualifiedName, superClassType);
+ if (superClass != null) {
+ return superClass;
+ }
+ }
+ List<InterfaceType> ifaces = classType.allInterfaces();
+ for (Iterator<InterfaceType> it = ifaces.iterator(); it.hasNext();) {
+ InterfaceType iface = it.next();
+ ReferenceType superClass = getSuperClass(baseQualifiedName, iface);
+ if (superClass != null) {
+ return superClass;
+ }
+ }
+ }
+
+ if (checkedType instanceof InterfaceType) {
+ List<InterfaceType> list = ((InterfaceType)checkedType).superinterfaces();
+ for (Iterator<InterfaceType> it = list.iterator(); it.hasNext();) {
+ InterfaceType superInterface = it.next();
+ ReferenceType superClass = getSuperClass(baseQualifiedName, superInterface);
+ if (superClass != null) {
+ return superClass;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static boolean valuesEqual(Value val1, Value val2) {
+ if (val1 == null) {
+ return val2 == null;
+ }
+ if (val2 == null) {
+ return false;
+ }
+ if (val1 instanceof StringReference && val2 instanceof StringReference) {
+ return ((StringReference)val1).value().equals(((StringReference)val2).value());
+ }
+ return val1.equals(val2);
+ }
+
+ public static String getValueOrErrorAsString(final EvaluationContext evaluationContext, Value value) {
+ try {
+ return getValueAsString(evaluationContext, value);
+ }
+ catch (EvaluateException e) {
+ return e.getMessage();
+ }
+ }
+
+ public static boolean isCharOrInteger(Value value) {
+ return value instanceof CharValue || isInteger(value);
+ }
+
+ private static Set<String> myCharOrIntegers;
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public static boolean isCharOrIntegerArray(Value value) {
+ if (value == null) return false;
+ if (myCharOrIntegers == null) {
+ myCharOrIntegers = new HashSet<String>();
+ myCharOrIntegers.add("C");
+ myCharOrIntegers.add("B");
+ myCharOrIntegers.add("S");
+ myCharOrIntegers.add("I");
+ myCharOrIntegers.add("J");
+ }
+
+ String signature = value.type().signature();
+ int i;
+ for (i = 0; signature.charAt(i) == '['; i++) ;
+ if (i == 0) return false;
+ signature = signature.substring(i, signature.length());
+ return myCharOrIntegers.contains(signature);
+ }
+
+ public static ClassFilter create(Element element) throws InvalidDataException {
+ ClassFilter filter = new ClassFilter();
+ filter.readExternal(element);
+ return filter;
+ }
+
+ private static boolean isFiltered(ClassFilter classFilter, String qName) {
+ if (!classFilter.isEnabled()) {
+ return false;
+ }
+ try {
+ if (classFilter.matches(qName)) {
+ return true;
+ }
+ }
+ catch (PatternSyntaxException e) {
+ LOG.debug(e);
+ }
+ return false;
+ }
+
+ public static boolean isFiltered(String qName, ClassFilter[] classFilters) {
+ return isFiltered(qName, Arrays.asList(classFilters));
+ }
+
+ public static boolean isFiltered(String qName, List<ClassFilter> classFilters) {
+ if(qName.indexOf('[') != -1) {
+ return false; //is array
+ }
+
+ for (ClassFilter filter : classFilters) {
+ if (isFiltered(filter, qName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static ClassFilter[] readFilters(List children) throws InvalidDataException {
+ if (children == null || children.size() == 0) {
+ return ClassFilter.EMPTY_ARRAY;
+ }
+ List<ClassFilter> classFiltersList = new ArrayList<ClassFilter>(children.size());
+ for (Iterator i = children.iterator(); i.hasNext();) {
+ final ClassFilter classFilter = new ClassFilter();
+ classFilter.readExternal((Element)i.next());
+ classFiltersList.add(classFilter);
+ }
+ return classFiltersList.toArray(new ClassFilter[classFiltersList.size()]);
+ }
+
+ public static void writeFilters(Element parentNode, @NonNls String tagName, ClassFilter[] filters) throws WriteExternalException {
+ for (ClassFilter filter : filters) {
+ Element element = new Element(tagName);
+ parentNode.addContent(element);
+ filter.writeExternal(element);
+ }
+ }
+
+ public static boolean filterEquals(ClassFilter[] filters1, ClassFilter[] filters2) {
+ if (filters1.length != filters2.length) {
+ return false;
+ }
+ final Set<ClassFilter> f1 = new HashSet<ClassFilter>(Math.max((int) (filters1.length/.75f) + 1, 16));
+ final Set<ClassFilter> f2 = new HashSet<ClassFilter>(Math.max((int) (filters2.length/.75f) + 1, 16));
+ for (ClassFilter filter : filters1) {
+ f1.add(filter);
+ }
+ for (ClassFilter aFilters2 : filters2) {
+ f2.add(aFilters2);
+ }
+ return f2.equals(f1);
+ }
+
+ private static boolean elementListsEqual(List<Element> l1, List<Element> l2) {
+ if(l1 == null) return l2 == null;
+ if(l2 == null) return false;
+
+ if(l1.size() != l2.size()) return false;
+
+ Iterator<Element> i1 = l1.iterator();
+ Iterator<Element> i2 = l2.iterator();
+
+ while (i2.hasNext()) {
+ Element elem1 = i1.next();
+ Element elem2 = i2.next();
+
+ if(!elementsEqual(elem1, elem2)) return false;
+ }
+ return true;
+ }
+
+ private static boolean attributeListsEqual(List<Attribute> l1, List<Attribute> l2) {
+ if(l1 == null) return l2 == null;
+ if(l2 == null) return false;
+
+ if(l1.size() != l2.size()) return false;
+
+ Iterator<Attribute> i1 = l1.iterator();
+ Iterator<Attribute> i2 = l2.iterator();
+
+ while (i2.hasNext()) {
+ Attribute attr1 = i1.next();
+ Attribute attr2 = i2.next();
+
+ if (!Comparing.equal(attr1.getName(), attr2.getName()) || !Comparing.equal(attr1.getValue(), attr2.getValue())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean elementsEqual(Element e1, Element e2) {
+ if(e1 == null) {
+ return e2 == null;
+ }
+ if (!Comparing.equal(e1.getName(), e2.getName())) {
+ return false;
+ }
+ if (!elementListsEqual ((List<Element>)e1.getChildren(), (List<Element>)e2.getChildren())) {
+ return false;
+ }
+ if (!attributeListsEqual((List<Attribute>)e1.getAttributes(), (List<Attribute>)e2.getAttributes())) {
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public static boolean externalizableEqual(JDOMExternalizable e1, JDOMExternalizable e2) {
+ Element root1 = new Element("root");
+ Element root2 = new Element("root");
+ try {
+ e1.writeExternal(root1);
+ }
+ catch (WriteExternalException e) {
+ LOG.debug(e);
+ }
+ try {
+ e2.writeExternal(root2);
+ }
+ catch (WriteExternalException e) {
+ LOG.debug(e);
+ }
+
+ return elementsEqual(root1, root2);
+ }
+
+ public static List<Pair<Breakpoint, Event>> getEventDescriptors(SuspendContextImpl suspendContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if(suspendContext == null || suspendContext.getEventSet() == null) {
+ return Collections.emptyList();
+ }
+ final List<Pair<Breakpoint, Event>> eventDescriptors = new ArrayList<Pair<Breakpoint, Event>>();
+
+ final RequestManagerImpl requestManager = suspendContext.getDebugProcess().getRequestsManager();
+ for (final Event event : suspendContext.getEventSet()) {
+ Requestor requestor = requestManager.findRequestor(event.request());
+ if (requestor instanceof Breakpoint) {
+ eventDescriptors.add(new Pair<Breakpoint, Event>((Breakpoint)requestor, event));
+ }
+ }
+ return eventDescriptors;
+ }
+
+ public static TextWithImports getEditorText(final Editor editor) {
+ if (editor == null) {
+ return null;
+ }
+ final Project project = editor.getProject();
+ if (project == null) return null;
+
+ String defaultExpression = editor.getSelectionModel().getSelectedText();
+ if (defaultExpression == null) {
+ int offset = editor.getCaretModel().getOffset();
+ PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+ if (psiFile != null) {
+ PsiElement elementAtCursor = psiFile.findElementAt(offset);
+ if (elementAtCursor != null) {
+ final EditorTextProvider textProvider = EditorTextProvider.EP.forLanguage(elementAtCursor.getLanguage());
+ if (textProvider != null) {
+ final TextWithImports editorText = textProvider.getEditorText(elementAtCursor);
+ if (editorText != null) return editorText;
+ }
+ }
+ }
+ }
+ else {
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, defaultExpression);
+ }
+ return null;
+ }
+
+ public abstract DebuggerTreeNode getSelectedNode (DataContext context);
+
+ public abstract EvaluatorBuilder getEvaluatorBuilder();
+
+ public abstract CompletionEditor createEditor(Project project, PsiElement context, @NonNls String recentsId);
+
+ @Nullable
+ public static CodeFragmentFactory getEffectiveCodeFragmentFactory(final PsiElement psiContext) {
+ final CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable<CodeFragmentFactory>() {
+ public CodeFragmentFactory compute() {
+ final List<CodeFragmentFactory> codeFragmentFactories = getCodeFragmentFactories(psiContext);
+ // the list always contains at least DefaultCodeFragmentFactory
+ return codeFragmentFactories.get(0);
+ }
+ });
+ return factory != null? new CodeFragmentFactoryContextWrapper(factory) : null;
+ }
+
+ private static class SigReader {
+ final String buffer;
+ int pos = 0;
+
+ SigReader(String s) {
+ buffer = s;
+ }
+
+ int get() {
+ return buffer.charAt(pos++);
+ }
+
+ int peek() {
+ return buffer.charAt(pos);
+ }
+
+ boolean eof() {
+ return buffer.length() <= pos;
+ }
+
+ @NonNls String getSignature() {
+ if (eof()) return "";
+
+ switch (get()) {
+ case 'Z':
+ return "boolean";
+ case 'B':
+ return "byte";
+ case 'C':
+ return "char";
+ case 'S':
+ return "short";
+ case 'I':
+ return "int";
+ case 'J':
+ return "long";
+ case 'F':
+ return "float";
+ case 'D':
+ return "double";
+ case 'V':
+ return "void";
+ case 'L':
+ int start = pos;
+ pos = buffer.indexOf(';', start) + 1;
+ LOG.assertTrue(pos > 0);
+ return buffer.substring(start, pos - 1).replace('/', '.');
+ case '[':
+ return getSignature() + "[]";
+ case '(':
+ StringBuffer result = new StringBuffer("(");
+ String separator = "";
+ while (peek() != ')') {
+ result.append(separator);
+ result.append(getSignature());
+ separator = ", ";
+ }
+ get();
+ result.append(")");
+ return getSignature() + " " + getClassName() + "." + getMethodName() + " " + result;
+ default:
+// LOG.assertTrue(false, "unknown signature " + buffer);
+ return null;
+ }
+ }
+
+ String getMethodName() {
+ return "";
+ }
+
+ String getClassName() {
+ return "";
+ }
+ }
+
+ public static String methodName(final Method m) {
+ return methodName(signatureToName(m.declaringType().signature()), m.name(), m.signature());
+ }
+
+ public static String methodName(final String className, final String methodName, final String signature) {
+ try {
+ return new SigReader(signature) {
+ String getMethodName() {
+ return methodName;
+ }
+
+ String getClassName() {
+ return className;
+ }
+ }.getSignature();
+ }
+ catch (Exception e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Internal error : unknown signature" + signature);
+ }
+ return className + "." + methodName;
+ }
+ }
+
+ public static String signatureToName(String s) {
+ return new SigReader(s).getSignature();
+ }
+
+ public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, double value) {
+ if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf(value);
+ }
+ if (PsiType.FLOAT.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((float)value);
+ }
+ return createValue(vm, expectedType, (long)value);
+ }
+
+ public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, long value) {
+ if (PsiType.LONG.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf(value);
+ }
+ if (PsiType.INT.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((int)value);
+ }
+ if (PsiType.SHORT.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((short)value);
+ }
+ if (PsiType.BYTE.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((byte)value);
+ }
+ if (PsiType.CHAR.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((char)value);
+ }
+ if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((double)value);
+ }
+ if (PsiType.FLOAT.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((float)value);
+ }
+ return null;
+ }
+
+ public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, boolean value) {
+ if (PsiType.BOOLEAN.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf(value);
+ }
+ return null;
+ }
+
+ public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, char value) {
+ if (PsiType.CHAR.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf(value);
+ }
+ if (PsiType.LONG.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((long)value);
+ }
+ if (PsiType.INT.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((int)value);
+ }
+ if (PsiType.SHORT.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((short)value);
+ }
+ if (PsiType.BYTE.getPresentableText().equals(expectedType)) {
+ return vm.mirrorOf((byte)value);
+ }
+ return null;
+ }
+
+ public static String truncateString(final String str) {
+ if (str.length() > MAX_LABEL_SIZE) {
+ return str.substring(0, MAX_LABEL_SIZE) + "...";
+ }
+ return str;
+ }
+
+ public static String getThreadStatusText(int statusId) {
+ switch (statusId) {
+ case ThreadReference.THREAD_STATUS_MONITOR:
+ return DebuggerBundle.message("status.thread.monitor");
+ case ThreadReference.THREAD_STATUS_NOT_STARTED:
+ return DebuggerBundle.message("status.thread.not.started");
+ case ThreadReference.THREAD_STATUS_RUNNING:
+ return DebuggerBundle.message("status.thread.running");
+ case ThreadReference.THREAD_STATUS_SLEEPING:
+ return DebuggerBundle.message("status.thread.sleeping");
+ case ThreadReference.THREAD_STATUS_UNKNOWN:
+ return DebuggerBundle.message("status.thread.unknown");
+ case ThreadReference.THREAD_STATUS_WAIT:
+ return DebuggerBundle.message("status.thread.wait");
+ case ThreadReference.THREAD_STATUS_ZOMBIE:
+ return DebuggerBundle.message("status.thread.zombie");
+ default:
+ return DebuggerBundle.message("status.thread.undefined");
+ }
+ }
+
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsImpl.java
new file mode 100644
index 0000000..269799d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsImpl.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.actions.DebuggerAction;
+import com.intellij.debugger.apiAdapters.TransportServiceWrapper;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.ui.CompletionEditor;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeExpression;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.execution.ExecutionException;
+import com.intellij.ide.util.TreeClassChooser;
+import com.intellij.ide.util.TreeClassChooserFactory;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.JDOMExternalizerUtil;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.util.net.NetUtils;
+import com.sun.jdi.Value;
+import org.jdom.Element;
+
+import java.io.IOException;
+
+public class DebuggerUtilsImpl extends DebuggerUtilsEx{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerUtilsImpl");
+
+ public PsiExpression substituteThis(PsiExpression expressionWithThis, PsiExpression howToEvaluateThis, Value howToEvaluateThisValue, StackFrameContext context)
+ throws EvaluateException {
+ return DebuggerTreeNodeExpression.substituteThis(expressionWithThis, howToEvaluateThis, howToEvaluateThisValue);
+ }
+
+ public EvaluatorBuilder getEvaluatorBuilder() {
+ return EvaluatorBuilderImpl.getInstance();
+ }
+
+ public DebuggerTreeNode getSelectedNode(DataContext context) {
+ return DebuggerAction.getSelectedNode(context);
+ }
+
+ public DebuggerContextImpl getDebuggerContext(DataContext context) {
+ return DebuggerAction.getDebuggerContext(context);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public Element writeTextWithImports(TextWithImports text) {
+ Element element = new Element("TextWithImports");
+
+ element.setAttribute("text", text.toExternalForm());
+ element.setAttribute("type", text.getKind() == CodeFragmentKind.EXPRESSION ? "expression" : "code fragment");
+ return element;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public TextWithImports readTextWithImports(Element element) {
+ LOG.assertTrue("TextWithImports".equals(element.getName()));
+
+ String text = element.getAttributeValue("text");
+ if ("expression".equals(element.getAttributeValue("type"))) {
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, text);
+ } else {
+ return new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, text);
+ }
+ }
+
+ public void writeTextWithImports(Element root, String name, TextWithImports value) {
+ LOG.assertTrue(value.getKind() == CodeFragmentKind.EXPRESSION);
+ JDOMExternalizerUtil.writeField(root, name, value.toExternalForm());
+ }
+
+ public TextWithImports readTextWithImports(Element root, String name) {
+ String s = JDOMExternalizerUtil.readField(root, name);
+ if(s == null) return null;
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, s);
+ }
+
+ public TextWithImports createExpressionWithImports(String expression) {
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression);
+ }
+
+ public PsiElement getContextElement(StackFrameContext context) {
+ return PositionUtil.getContextElement(context);
+ }
+
+ public PsiClass chooseClassDialog(String title, Project project) {
+ TreeClassChooser dialog = TreeClassChooserFactory.getInstance(project).createAllProjectScopeChooser(title);
+ dialog.showDialog();
+ return dialog.getSelected();
+ }
+
+ public CompletionEditor createEditor(Project project, PsiElement context, String recentsId) {
+ return new DebuggerExpressionComboBox(project, context, recentsId, DefaultCodeFragmentFactory.getInstance());
+ }
+
+ public String findAvailableDebugAddress(final boolean useSockets) throws ExecutionException {
+ final TransportServiceWrapper transportService = TransportServiceWrapper.getTransportService(useSockets);
+
+ if(useSockets) {
+ final int freePort;
+ try {
+ freePort = NetUtils.findAvailableSocketPort();
+ }
+ catch (IOException e) {
+ throw new ExecutionException(DebugProcessImpl.processError(e));
+ }
+ return Integer.toString(freePort);
+ }
+
+ try {
+ String address = transportService.startListening();
+ transportService.stopListening(address);
+ return address;
+ }
+ catch (IOException e) {
+ throw new ExecutionException(DebugProcessImpl.processError(e));
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/EditorTextProvider.java b/java/debugger/impl/src/com/intellij/debugger/impl/EditorTextProvider.java
new file mode 100644
index 0000000..1667b74
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/EditorTextProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2011 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.lang.LanguageExtension;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Provides text in the editor for Evaluate expression action
+ * @author Maxim.Medvedev
+ */
+public interface EditorTextProvider {
+ LanguageExtension<EditorTextProvider> EP = new LanguageExtension<EditorTextProvider>("com.intellij.debuggerEditorTextProvider");
+
+ @Nullable
+ TextWithImports getEditorText(PsiElement elementAtCaret);
+
+ @Nullable
+ Pair<PsiElement, TextRange> findExpression(PsiElement elementAtCaret, boolean allowMethodCalls);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/EventQueue.java b/java/debugger/impl/src/com/intellij/debugger/impl/EventQueue.java
new file mode 100644
index 0000000..a7bd73a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/EventQueue.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.openapi.diagnostic.Logger;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+
+public class EventQueue<E> {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.EventQueue");
+
+ private final LinkedList[] myEvents;
+ private final ReentrantLock myLock;
+ private final Condition myEventsAvailable;
+
+ private volatile E myCurrentEvent;
+
+ private volatile boolean myIsClosed = false;
+
+ public EventQueue (int countPriorities) {
+ myLock = new ReentrantLock();
+ myEventsAvailable = myLock.newCondition();
+ myEvents = new LinkedList[countPriorities];
+ for (int i = 0; i < myEvents.length; i++) {
+ myEvents[i] = new LinkedList<E>();
+ }
+ }
+
+ public boolean pushBack(@NotNull E event, int priority) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("pushBack event " + event);
+ }
+
+ myLock.lock();
+ try {
+ if (isClosed()) {
+ return false;
+ }
+ getEventsList(priority).addFirst(event);
+ myEventsAvailable.signalAll();
+ }
+ finally {
+ myLock.unlock();
+ }
+ return true;
+ }
+
+ public boolean put(@NotNull E event, int priority) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("put event " + event);
+ }
+
+ myLock.lock();
+ try {
+ if (isClosed()) {
+ return false;
+ }
+ getEventsList(priority).offer(event);
+ myEventsAvailable.signalAll();
+ }
+ finally {
+ myLock.unlock();
+ }
+ return true;
+ }
+
+ private LinkedList<E> getEventsList(final int priority) {
+ return (LinkedList<E>)myEvents[priority];
+ }
+
+ public void close(){
+ myLock.lock();
+ try {
+ myIsClosed = true;
+ myEventsAvailable.signalAll();
+ }
+ finally {
+ myLock.unlock();
+ }
+ }
+
+ private E getEvent() throws EventQueueClosedException {
+ myLock.lock();
+ try {
+ while (true) {
+ if(myIsClosed) {
+ throw new EventQueueClosedException();
+ }
+ for (int i = 0; i < myEvents.length; i++) {
+ final E event = getEventsList(i).poll();
+ if (event != null) {
+ return event;
+ }
+ }
+ myEventsAvailable.awaitUninterruptibly();
+ }
+ }
+ finally {
+ myLock.unlock();
+ }
+ }
+
+ public E get() throws EventQueueClosedException {
+ try {
+ return myCurrentEvent = getEvent();
+ }
+ catch (EventQueueClosedException e) {
+ myCurrentEvent = null; // cleanup
+ throw e;
+ }
+ }
+
+ public boolean isClosed() {
+ return myIsClosed;
+ }
+
+ public E getCurrentEvent() {
+ return myCurrentEvent;
+ }
+
+ public List<E> clearQueue() {
+ final List<E> allEvents = new ArrayList<E>();
+ for (int i = 0; i < myEvents.length; i++) {
+ final LinkedList<E> eventList = getEventsList(i);
+ while (!eventList.isEmpty()) {
+ allEvents.add(eventList.poll());
+ }
+ }
+ return allEvents;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/EventQueueClosedException.java b/java/debugger/impl/src/com/intellij/debugger/impl/EventQueueClosedException.java
new file mode 100644
index 0000000..d58b663
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/EventQueueClosedException.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+/**
+ * @author lex
+ */
+public class EventQueueClosedException extends Exception {
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerParametersRunnerConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerParametersRunnerConfigurable.java
new file mode 100644
index 0000000..b0cb440
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerParametersRunnerConfigurable.java
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.execution.ExecutionException;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.xdebugger.impl.settings.DebuggerConfigurable;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class GenericDebuggerParametersRunnerConfigurable extends SettingsEditor<GenericDebuggerRunnerSettings> {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.GenericDebuggerParametersRunnerConfigurable");
+ private JPanel myPanel;
+ private JTextField myAddressField;
+ private JPanel myShMemPanel;
+ private JPanel myPortPanel;
+ private JTextField myPortField;
+ private boolean myIsLocal = false;
+ private JButton myDebuggerSettings;
+ private JRadioButton mySocketTransport;
+ private JRadioButton myShmemTransport;
+ private JPanel myTransportPanel;
+
+ public GenericDebuggerParametersRunnerConfigurable(final Project project) {
+ myDebuggerSettings.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ShowSettingsUtil.getInstance().showSettingsDialog(project, DebuggerConfigurable.DISPLAY_NAME);
+ if (myIsLocal) {
+ setTransport(DebuggerSettings.getInstance().DEBUGGER_TRANSPORT);
+ }
+ suggestAvailablePortIfNotSpecified();
+ updateUI();
+ }
+ });
+
+ final ActionListener listener = new ActionListener() {
+ public void actionPerformed(final ActionEvent e) {
+ suggestAvailablePortIfNotSpecified();
+ updateUI();
+ myPanel.repaint();
+ }
+ };
+ mySocketTransport.addActionListener(listener);
+ myShmemTransport.addActionListener(listener);
+
+ updateUI();
+
+ myTransportPanel.setVisible(false);
+
+ ButtonGroup group = new ButtonGroup();
+ group.add(mySocketTransport);
+ group.add(myShmemTransport);
+ }
+
+ private boolean isSocket() {
+ return getTransport() == DebuggerSettings.SOCKET_TRANSPORT;
+ }
+
+ @NotNull
+ public JComponent createEditor() {
+ return myPanel;
+ }
+
+ private void updateUI() {
+ myPortPanel.setVisible(isSocket());
+ myShMemPanel.setVisible(!isSocket());
+ myAddressField.setEditable(!myIsLocal);
+ myPortField.setEditable(!myIsLocal);
+ mySocketTransport.setEnabled(!myIsLocal);
+ myShmemTransport.setEnabled(!myIsLocal);
+ }
+
+ public void disposeEditor() {
+ }
+
+ public void resetEditorFrom(GenericDebuggerRunnerSettings runnerSettings) {
+ setIsLocal(runnerSettings.LOCAL);
+ setTransport(runnerSettings.getTransport());
+ setPort(StringUtil.notNullize(runnerSettings.getDebugPort()));
+ suggestAvailablePortIfNotSpecified();
+ updateUI();
+ }
+
+ private void suggestAvailablePortIfNotSpecified() {
+ String port = getPort();
+ boolean portSpecified = !StringUtil.isEmpty(port);
+ boolean isSocketTransport = getTransport() == DebuggerSettings.SOCKET_TRANSPORT;
+ if (isSocketTransport) {
+ try {
+ Integer.parseInt(port);
+ }
+ catch (NumberFormatException e) {
+ portSpecified = false;
+ }
+ }
+
+ if (!portSpecified) {
+ try {
+ setPort(DebuggerUtils.getInstance().findAvailableDebugAddress(isSocketTransport));
+ }
+ catch (ExecutionException e) {
+ LOG.info(e);
+ }
+ }
+ }
+
+ private int getTransport() {
+ if(myIsLocal) {
+ return DebuggerSettings.getInstance().DEBUGGER_TRANSPORT;
+ }
+ else {
+ return mySocketTransport.isSelected() ? DebuggerSettings.SOCKET_TRANSPORT : DebuggerSettings.SHMEM_TRANSPORT;
+ }
+ }
+
+ private String getPort() {
+ if (isSocket()) {
+ return myPortField.getText();
+ }
+ else {
+ return myAddressField.getText();
+ }
+ }
+
+ private void checkPort() throws ConfigurationException {
+ if (isSocket() && myPortField.getText().length() > 0) {
+ try {
+ final int port = Integer.parseInt(myPortField.getText());
+ if (port < 0 || port > 0xffff) {
+ throw new NumberFormatException();
+ }
+ }
+ catch (NumberFormatException e) {
+ throw new ConfigurationException(DebuggerBundle.message("error.text.invalid.port.0", myPortField.getText()));
+ }
+ }
+ }
+
+ private void setTransport(int transport) {
+ mySocketTransport.setSelected(transport == DebuggerSettings.SOCKET_TRANSPORT);
+ myShmemTransport.setSelected(transport != DebuggerSettings.SOCKET_TRANSPORT);
+ }
+
+ private void setIsLocal(boolean b) {
+ myTransportPanel.setVisible(true);
+ myDebuggerSettings.setVisible(b);
+ myIsLocal = b;
+ }
+
+ private void setPort(String port) {
+ if (isSocket()) {
+ myPortField.setText(port);
+ }
+ else {
+ myAddressField.setText(port);
+ }
+ }
+
+ public void applyEditorTo(GenericDebuggerRunnerSettings runnerSettings) throws ConfigurationException {
+ runnerSettings.LOCAL = myIsLocal;
+ checkPort();
+ runnerSettings.setDebugPort(getPort());
+ if (!myIsLocal) {
+ runnerSettings.setTransport(getTransport());
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerRunner.java b/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerRunner.java
new file mode 100644
index 0000000..fd25d96
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerRunner.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.DebuggerPanelsManager;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.*;
+import com.intellij.execution.executors.DefaultDebugExecutor;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.JavaPatchableProgramRunner;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class GenericDebuggerRunner extends JavaPatchableProgramRunner<GenericDebuggerRunnerSettings> {
+ public boolean canRun(@NotNull final String executorId, @NotNull final RunProfile profile) {
+ return executorId.equals(DefaultDebugExecutor.EXECUTOR_ID) && profile instanceof ModuleRunProfile
+ && !(profile instanceof RunConfigurationWithSuppressedDefaultDebugAction);
+ }
+
+ @NotNull
+ public String getRunnerId() {
+ return DebuggingRunnerData.DEBUGGER_RUNNER_ID;
+ }
+
+ protected RunContentDescriptor doExecute(final Project project, final Executor executor, final RunProfileState state, final RunContentDescriptor contentToReuse,
+ final ExecutionEnvironment env) throws ExecutionException {
+ FileDocumentManager.getInstance().saveAllDocuments();
+ return createContentDescriptor(project, executor, state, contentToReuse, env);
+ }
+
+ @Nullable
+ protected RunContentDescriptor createContentDescriptor(Project project, Executor executor, RunProfileState state,
+ RunContentDescriptor contentToReuse,
+ ExecutionEnvironment env) throws ExecutionException {
+ if (state instanceof JavaCommandLine) {
+ final JavaParameters parameters = ((JavaCommandLine)state).getJavaParameters();
+ runCustomPatchers(parameters, state.getRunnerSettings(), executor);
+ RemoteConnection connection = DebuggerManagerImpl.createDebugParameters(parameters, true, DebuggerSettings.getInstance().DEBUGGER_TRANSPORT, "", false);
+ return attachVirtualMachine(project, executor, state, contentToReuse, env, connection, true);
+ }
+ if (state instanceof PatchedRunnableState) {
+ final RemoteConnection connection = doPatch(new JavaParameters(), state.getRunnerSettings());
+ return attachVirtualMachine(project, executor, state, contentToReuse, env, connection, true);
+ }
+ if (state instanceof RemoteState) {
+ final RemoteConnection connection = createRemoteDebugConnection((RemoteState)state, state.getRunnerSettings());
+ return attachVirtualMachine(project, executor, state, contentToReuse, env, connection, false);
+ }
+
+ return null;
+ }
+
+ @Nullable
+ protected RunContentDescriptor attachVirtualMachine(Project project, Executor executor, RunProfileState state,
+ RunContentDescriptor contentToReuse,
+ ExecutionEnvironment env, RemoteConnection connection, boolean pollConnection)
+ throws ExecutionException {
+ final DebuggerPanelsManager manager = DebuggerPanelsManager.getInstance(project);
+ return manager.attachVirtualMachine(executor, this, env, state, contentToReuse, connection, pollConnection);
+ }
+
+ private static RemoteConnection createRemoteDebugConnection(RemoteState connection, final RunnerSettings settings) {
+ final RemoteConnection remoteConnection = connection.getRemoteConnection();
+
+ GenericDebuggerRunnerSettings debuggerRunnerSettings = ((GenericDebuggerRunnerSettings)settings.getData());
+
+ if (debuggerRunnerSettings != null) {
+ remoteConnection.setUseSockets(debuggerRunnerSettings.getTransport() == DebuggerSettings.SOCKET_TRANSPORT);
+ remoteConnection.setAddress(debuggerRunnerSettings.DEBUG_PORT);
+ }
+
+ return remoteConnection;
+ }
+
+ public GenericDebuggerRunnerSettings createConfigurationData(ConfigurationInfoProvider settingsProvider) {
+ return new GenericDebuggerRunnerSettings();
+ }
+
+ public void patch(JavaParameters javaParameters, RunnerSettings settings, final boolean beforeExecution) throws ExecutionException {
+ doPatch(javaParameters, settings);
+ runCustomPatchers(javaParameters, settings, Executor.EXECUTOR_EXTENSION_NAME.findExtension(DefaultDebugExecutor.class));
+ }
+
+ private static RemoteConnection doPatch(final JavaParameters javaParameters, final RunnerSettings settings) throws ExecutionException {
+ final GenericDebuggerRunnerSettings debuggerSettings = ((GenericDebuggerRunnerSettings)settings.getData());
+ if (StringUtil.isEmpty(debuggerSettings.getDebugPort())) {
+ debuggerSettings.setDebugPort(DebuggerUtils.getInstance().findAvailableDebugAddress(debuggerSettings.getTransport() == DebuggerSettings.SOCKET_TRANSPORT));
+ }
+ return DebuggerManagerImpl.createDebugParameters(javaParameters, debuggerSettings, false);
+ }
+
+ public SettingsEditor<GenericDebuggerRunnerSettings> getSettingsEditor(final Executor executor, RunConfiguration configuration) {
+ if (configuration instanceof RunConfigurationWithRunnerSettings) {
+ if (((RunConfigurationWithRunnerSettings)configuration).isSettingsNeeded()) {
+ return new GenericDebuggerParametersRunnerConfigurable(configuration.getProject());
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerRunnerSettings.java b/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerRunnerSettings.java
new file mode 100644
index 0000000..b4a03c6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/GenericDebuggerRunnerSettings.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.execution.configurations.DebuggingRunnerData;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import org.jdom.Element;
+
+public class GenericDebuggerRunnerSettings implements JDOMExternalizable, DebuggingRunnerData {
+ public String DEBUG_PORT = "";
+ public int TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT;
+ public boolean LOCAL = true;
+
+ public GenericDebuggerRunnerSettings() {
+ }
+
+ public String getDebugPort() {
+ return DEBUG_PORT;
+ }
+
+ public boolean isRemote() {
+ return !LOCAL;
+ }
+
+ public void setLocal(boolean isLocal) {
+ LOCAL = isLocal;
+ }
+
+ public void setDebugPort(String port) {
+ DEBUG_PORT = port;
+ }
+
+ public void setTransport(int transport) {
+ TRANSPORT = transport;
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ DefaultJDOMExternalizer.readExternal(this, element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ }
+
+ public int getTransport() {
+ if (LOCAL) {
+ return DebuggerSettings.getInstance().DEBUGGER_TRANSPORT;
+ }
+ else {
+ return TRANSPORT;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapFile.java b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapFile.java
new file mode 100644
index 0000000..db0a0fb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapFile.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import java.io.File;
+
+/**
+ * User: lex
+ * Date: Nov 18, 2003
+ * Time: 2:23:38 PM
+ */
+public class HotSwapFile {
+ final File file;
+
+ public HotSwapFile(File file) {
+ this.file = file;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java
new file mode 100644
index 0000000..b7cd3f9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.ex.CompilerPathsEx;
+import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class HotSwapManager extends AbstractProjectComponent {
+ private final Map<DebuggerSession, Long> myTimeStamps = new HashMap<DebuggerSession, Long>();
+ private static final String CLASS_EXTENSION = ".class";
+
+ public HotSwapManager(Project project, DebuggerManagerEx manager) {
+ super(project);
+ manager.addDebuggerManagerListener(new DebuggerManagerAdapter() {
+ public void sessionCreated(DebuggerSession session) {
+ myTimeStamps.put(session, Long.valueOf(System.currentTimeMillis()));
+ }
+
+ public void sessionRemoved(DebuggerSession session) {
+ myTimeStamps.remove(session);
+ }
+ });
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "HotSwapManager";
+ }
+
+ private long getTimeStamp(DebuggerSession session) {
+ Long tStamp = myTimeStamps.get(session);
+ return tStamp != null ? tStamp.longValue() : 0;
+ }
+
+ void setTimeStamp(DebuggerSession session, long tStamp) {
+ myTimeStamps.put(session, Long.valueOf(tStamp));
+ }
+
+ public Map<String, HotSwapFile> scanForModifiedClasses(final DebuggerSession session, final HotSwapProgress progress, final boolean scanWithVFS) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ final long timeStamp = getTimeStamp(session);
+ final Map<String, HotSwapFile> modifiedClasses = new HashMap<String, HotSwapFile>();
+
+ if (scanWithVFS) {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final List<VirtualFile> allDirs = OrderEnumerator.orderEntries(myProject).withoutSdk().withoutLibraries().getPathsList().getRootDirs();
+ CompilerPathsEx.visitFiles(allDirs, new CompilerPathsEx.FileVisitor() {
+ protected void acceptDirectory(final VirtualFile file, final String fileRoot, final String filePath) {
+ if (!progress.isCancelled()) {
+ progress.setText(DebuggerBundle.message("progress.hotswap.scanning.path", filePath));
+ super.acceptDirectory(file, fileRoot, filePath);
+ }
+ }
+
+ protected void acceptFile(VirtualFile file, String fileRoot, String filePath) {
+ if (progress.isCancelled()) {
+ return;
+ }
+ if (file.getTimeStamp() > timeStamp && StdFileTypes.CLASS.equals(file.getFileType())) {
+ //noinspection HardCodedStringLiteral
+ if (SystemInfo.isFileSystemCaseSensitive ? filePath.endsWith(CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(filePath, CLASS_EXTENSION)) {
+ progress.setText(DebuggerBundle.message("progress.hotswap.scanning.path", filePath));
+ //noinspection HardCodedStringLiteral
+ final String qualifiedName = filePath.substring(fileRoot.length() + 1, filePath.length() - CLASS_EXTENSION.length()).replace('/', '.');
+ modifiedClasses.put(qualifiedName, new HotSwapFile(new File(filePath)));
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+ else {
+ final List<File> outputRoots = new ArrayList<File>();
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final List<VirtualFile> allDirs = OrderEnumerator.orderEntries(myProject).withoutSdk().withoutLibraries().getPathsList().getRootDirs();
+ for (VirtualFile dir : allDirs) {
+ outputRoots.add(new File(dir.getPath()));
+ }
+ }
+ });
+ for (File root : outputRoots) {
+ final String rootPath = FileUtil.toCanonicalPath(root.getPath());
+ collectModifiedClasses(root, rootPath, rootPath + "/", modifiedClasses, progress, timeStamp);
+ }
+ }
+
+
+ return modifiedClasses;
+ }
+
+ private static boolean collectModifiedClasses(File file, String filePath, String rootPath, Map<String, HotSwapFile> container, HotSwapProgress progress, long timeStamp) {
+ if (progress.isCancelled()) {
+ return false;
+ }
+ final File[] files = file.listFiles();
+ if (files != null) {
+ for (File child : files) {
+ if (!collectModifiedClasses(child, filePath + "/" + child.getName(), rootPath, container, progress, timeStamp)) {
+ return false;
+ }
+ }
+ }
+ else { // not a dir
+ if (SystemInfo.isFileSystemCaseSensitive? StringUtil.endsWith(filePath, CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(filePath, CLASS_EXTENSION)) {
+ if (file.lastModified() > timeStamp) {
+ progress.setText(DebuggerBundle.message("progress.hotswap.scanning.path", filePath));
+ //noinspection HardCodedStringLiteral
+ final String qualifiedName = filePath.substring(rootPath.length(), filePath.length() - CLASS_EXTENSION.length()).replace('/', '.');
+ container.put(qualifiedName, new HotSwapFile(file));
+ }
+ }
+ }
+ return true;
+ }
+
+ public static HotSwapManager getInstance(Project project) {
+ return project.getComponent(HotSwapManager.class);
+ }
+
+ private void reloadClasses(DebuggerSession session, Map<String, HotSwapFile> classesToReload, HotSwapProgress progress) {
+ final long newSwapTime = System.currentTimeMillis();
+ new ReloadClassesWorker(session, progress).reloadClasses(classesToReload);
+ setTimeStamp(session, newSwapTime);
+ }
+
+ public static Map<DebuggerSession, Map<String, HotSwapFile>> findModifiedClasses(List<DebuggerSession> sessions, Map<String, List<String>> generatedPaths) {
+ final Map<DebuggerSession, Map<String, HotSwapFile>> result = new java.util.HashMap<DebuggerSession, Map<String, HotSwapFile>>();
+ List<Pair<DebuggerSession, Long>> sessionWithStamps = new ArrayList<Pair<DebuggerSession, Long>>();
+ for (DebuggerSession session : sessions) {
+ sessionWithStamps.add(new Pair<DebuggerSession, Long>(session, getInstance(session.getProject()).getTimeStamp(session)));
+ }
+ for (Map.Entry<String, List<String>> entry : generatedPaths.entrySet()) {
+ final File root = new File(entry.getKey());
+ for (String relativePath : entry.getValue()) {
+ if (SystemInfo.isFileSystemCaseSensitive? StringUtil.endsWith(relativePath, CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(relativePath, CLASS_EXTENSION)) {
+ final String qualifiedName = relativePath.substring(0, relativePath.length() - CLASS_EXTENSION.length()).replace('/', '.');
+ final HotSwapFile hotswapFile = new HotSwapFile(new File(root, relativePath));
+ final long fileStamp = hotswapFile.file.lastModified();
+
+ for (Pair<DebuggerSession, Long> pair : sessionWithStamps) {
+ final DebuggerSession session = pair.first;
+ if (fileStamp > pair.second) {
+ Map<String, HotSwapFile> container = result.get(session);
+ if (container == null) {
+ container = new java.util.HashMap<String, HotSwapFile>();
+ result.put(session, container);
+ }
+ container.put(qualifiedName, hotswapFile);
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+
+ public static Map<DebuggerSession, Map<String, HotSwapFile>> scanForModifiedClasses(final List<DebuggerSession> sessions, final HotSwapProgress swapProgress, final boolean scanWithVFS) {
+ final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses = new HashMap<DebuggerSession, Map<String, HotSwapFile>>();
+
+ final MultiProcessCommand scanClassesCommand = new MultiProcessCommand();
+
+ swapProgress.setCancelWorker(new Runnable() {
+ public void run() {
+ scanClassesCommand.cancel();
+ }
+ });
+
+ for (final DebuggerSession debuggerSession : sessions) {
+ if (debuggerSession.isAttached()) {
+ scanClassesCommand.addCommand(debuggerSession.getProcess(), new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ swapProgress.setDebuggerSession(debuggerSession);
+ final Map<String, HotSwapFile> sessionClasses = getInstance(swapProgress.getProject()).scanForModifiedClasses(debuggerSession, swapProgress, scanWithVFS);
+ if (!sessionClasses.isEmpty()) {
+ modifiedClasses.put(debuggerSession, sessionClasses);
+ }
+ }
+ });
+ }
+ }
+
+ swapProgress.setTitle(DebuggerBundle.message("progress.hotswap.scanning.classes"));
+ scanClassesCommand.run();
+
+ return swapProgress.isCancelled() ? new HashMap<DebuggerSession, Map<String, HotSwapFile>>() : modifiedClasses;
+ }
+
+ public static void reloadModifiedClasses(final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses, final HotSwapProgress reloadClassesProgress) {
+ final MultiProcessCommand reloadClassesCommand = new MultiProcessCommand();
+
+ reloadClassesProgress.setCancelWorker(new Runnable() {
+ public void run() {
+ reloadClassesCommand.cancel();
+ }
+ });
+
+ for (final DebuggerSession debuggerSession : modifiedClasses.keySet()) {
+ reloadClassesCommand.addCommand(debuggerSession.getProcess(), new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ reloadClassesProgress.setDebuggerSession(debuggerSession);
+ getInstance(reloadClassesProgress.getProject()).reloadClasses(
+ debuggerSession, modifiedClasses.get(debuggerSession), reloadClassesProgress
+ );
+ }
+ });
+ }
+
+ reloadClassesProgress.setTitle(DebuggerBundle.message("progress.hotswap.reloading"));
+ reloadClassesCommand.run();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapProgress.java b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapProgress.java
new file mode 100644
index 0000000..55b3fe1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapProgress.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.openapi.project.Project;
+
+/**
+ * User: lex
+ * Date: Nov 18, 2003
+ * Time: 2:20:18 PM
+ */
+public abstract class HotSwapProgress {
+ private final Project myProject;
+ private Runnable myCancelWorker;
+ private volatile boolean myIsCancelled;
+
+ public HotSwapProgress(Project project) {
+ myProject = project;
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ public abstract void addMessage(DebuggerSession session, final int type, final String text);
+
+ public abstract void setText(String text);
+
+ public abstract void setTitle(String title);
+
+ public abstract void setFraction(double v);
+
+ public void setCancelWorker(Runnable cancel) {
+ myCancelWorker = cancel;
+ }
+
+ public void cancel() {
+ myIsCancelled = true;
+ if(myCancelWorker != null) {
+ myCancelWorker.run();
+ }
+ }
+
+ public void finished() {
+ }
+
+ public abstract void setDebuggerSession(DebuggerSession debuggerSession);
+
+ public boolean isCancelled() {
+ return myIsCancelled;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/InvokeAndWaitThread.java b/java/debugger/impl/src/com/intellij/debugger/impl/InvokeAndWaitThread.java
new file mode 100644
index 0000000..3576c86
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/InvokeAndWaitThread.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+/**
+ * @author lex
+ */
+public abstract class InvokeAndWaitThread<E extends DebuggerTask> extends InvokeThread<E> {
+
+ public InvokeAndWaitThread() {
+ super();
+ }
+
+ /**
+ * !!! Do not remove this code !!!
+ * Otherwise it will be impossible to override schedule method
+ */
+ public boolean schedule(E e) {
+ return super.schedule(e);
+ }
+
+ public boolean pushBack(E e) {
+ return super.pushBack(e);
+ }
+
+ public void invokeAndWait(final E runnable) {
+ runnable.hold();
+ schedule(runnable);
+ runnable.waitFor();
+ }
+}
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java b/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java
new file mode 100644
index 0000000..84ed730
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.VMDisconnectedException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.concurrent.*;
+
+/**
+ * @author lex
+ */
+public abstract class InvokeThread<E extends PrioritizedTask> {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.InvokeThread");
+
+ private static final ThreadLocal<WorkerThreadRequest> ourWorkerRequest = new ThreadLocal<WorkerThreadRequest>();
+
+ public static final class WorkerThreadRequest<E extends PrioritizedTask> implements Runnable {
+ private final InvokeThread<E> myOwner;
+ private volatile Future<?> myRequestFuture;
+
+ WorkerThreadRequest(InvokeThread<E> owner) {
+ myOwner = owner;
+ }
+
+ public void run() {
+ synchronized (this) {
+ while (myRequestFuture == null) {
+ try {
+ wait();
+ }
+ catch (InterruptedException ignore) {
+ }
+ }
+ }
+ ourWorkerRequest.set(this);
+ try {
+ myOwner.run(this);
+ }
+ finally {
+ ourWorkerRequest.set(null);
+ boolean b = Thread.interrupted(); // reset interrupted status to return into pool
+ }
+ }
+
+ public void interrupt() {
+ assert myRequestFuture != null;
+ myRequestFuture.cancel( true );
+ }
+
+ public boolean isInterrupted() {
+ assert myRequestFuture != null;
+ return myRequestFuture.isCancelled() || myRequestFuture.isDone();
+ }
+
+ public void join() throws InterruptedException, ExecutionException {
+ assert myRequestFuture != null;
+ try {
+ myRequestFuture.get();
+ }
+ catch(CancellationException ignored) {
+ }
+ }
+
+ public void join(long timeout) throws InterruptedException, ExecutionException {
+ assert myRequestFuture != null;
+ try {
+ myRequestFuture.get(timeout, TimeUnit.MILLISECONDS);
+ }
+ catch (TimeoutException ignored) {
+ }
+ catch (CancellationException ignored) {
+ }
+ }
+
+ final void setRequestFuture(Future<?> requestFuture) {
+ synchronized (this) {
+ myRequestFuture = requestFuture;
+ notifyAll();
+ }
+ }
+
+ public InvokeThread<E> getOwner() {
+ return myOwner;
+ }
+
+ public boolean isDone() {
+ assert myRequestFuture != null;
+ return myRequestFuture.isDone() && ourWorkerRequest.get() == null;
+ }
+ }
+
+ protected final EventQueue<E> myEvents;
+
+ private volatile WorkerThreadRequest myCurrentRequest = null;
+
+ public InvokeThread() {
+ myEvents = new EventQueue<E>(PrioritizedTask.Priority.values().length);
+ startNewWorkerThread();
+ }
+
+ protected abstract void processEvent(E e);
+
+ protected void startNewWorkerThread() {
+ final WorkerThreadRequest workerRequest = new WorkerThreadRequest<E>(this);
+ myCurrentRequest = workerRequest;
+ workerRequest.setRequestFuture( ApplicationManager.getApplication().executeOnPooledThread(workerRequest) );
+ }
+
+ private void run(@NotNull WorkerThreadRequest threadRequest) {
+ while(true) {
+ try {
+ if(threadRequest.isInterrupted()) {
+ break;
+ }
+
+ final WorkerThreadRequest currentRequest = getCurrentRequest();
+ if(currentRequest != threadRequest) {
+ LOG.error("Expected " + threadRequest + " instead of " + currentRequest);
+ if (currentRequest != null && !currentRequest.isDone()) {
+ continue; // ensure events are processed by one thread at a time
+ }
+ }
+
+ processEvent(myEvents.get());
+ }
+ catch (VMDisconnectedException e) {
+ break;
+ }
+ catch (EventQueueClosedException e) {
+ final List<E> unprocessed = myEvents.clearQueue();
+ for (E event : unprocessed) {
+ try {
+ processEvent(event);
+ }
+ catch (Throwable ignored) {
+ }
+ }
+ break;
+ }
+ catch (RuntimeException e) {
+ if(e.getCause() instanceof InterruptedException) {
+ break;
+ }
+ LOG.error(e);
+ }
+ catch (Throwable e) {
+ LOG.error(e);
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Request " + this.toString() + " exited");
+ }
+ }
+
+ protected static InvokeThread currentThread() {
+ final WorkerThreadRequest request = getCurrentThreadRequest();
+ return request != null? request.getOwner() : null;
+ }
+
+ public boolean schedule(E r) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("schedule " + r + " in " + this);
+ }
+ return myEvents.put(r, r.getPriority().ordinal());
+ }
+
+ public boolean pushBack(E r) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("pushBack " + r + " in " + this);
+ }
+ return myEvents.pushBack(r, r.getPriority().ordinal());
+ }
+
+ protected void switchToRequest(WorkerThreadRequest newWorkerThread) {
+ final WorkerThreadRequest request = getCurrentThreadRequest();
+ LOG.assertTrue(request != null);
+ myCurrentRequest = newWorkerThread;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Closing " + request + " new request = " + newWorkerThread);
+ }
+
+ request.interrupt();
+ }
+
+ public WorkerThreadRequest getCurrentRequest() {
+ return myCurrentRequest;
+ }
+
+ public static WorkerThreadRequest getCurrentThreadRequest() {
+ return ourWorkerRequest.get();
+ }
+
+ public void close() {
+ myEvents.close();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Closing evaluation");
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java
new file mode 100644
index 0000000..e97b4bf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2000-2011 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Maxim.Medvedev
+ */
+public class JavaEditorTextProviderImpl implements EditorTextProvider {
+ private static final Logger LOG = Logger.getInstance(JavaEditorTextProviderImpl.class);
+
+ @Override
+ public TextWithImports getEditorText(PsiElement elementAtCaret) {
+ String result = null;
+ PsiElement element = findExpression(elementAtCaret);
+ if (element != null) {
+ if (element instanceof PsiReferenceExpression) {
+ final PsiReferenceExpression reference = (PsiReferenceExpression)element;
+ if (reference.getQualifier() == null) {
+ final PsiElement resolved = reference.resolve();
+ if (resolved instanceof PsiEnumConstant) {
+ final PsiEnumConstant enumConstant = (PsiEnumConstant)resolved;
+ final PsiClass enumClass = enumConstant.getContainingClass();
+ if (enumClass != null) {
+ result = enumClass.getName() + "." + enumConstant.getName();
+ }
+ }
+ }
+ }
+ if (result == null) {
+ result = element.getText();
+ }
+ }
+ return result != null? new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, result) : null;
+ }
+
+ @Nullable
+ private static PsiElement findExpression(PsiElement element) {
+ if (!(element instanceof PsiIdentifier || element instanceof PsiKeyword)) {
+ return null;
+ }
+ PsiElement parent = element.getParent();
+ if (parent instanceof PsiVariable) {
+ return element;
+ }
+ if (parent instanceof PsiReferenceExpression) {
+ if (parent.getParent() instanceof PsiCallExpression) return parent.getParent();
+ return parent;
+ }
+ if (parent instanceof PsiThisExpression) {
+ return parent;
+ }
+ return null;
+ }
+
+ @Nullable
+ public Pair<PsiElement, TextRange> findExpression(PsiElement element, boolean allowMethodCalls) {
+ if (!(element instanceof PsiIdentifier || element instanceof PsiKeyword)) {
+ return null;
+ }
+
+ PsiElement expression = null;
+ PsiElement parent = element.getParent();
+ if (parent instanceof PsiVariable) {
+ expression = element;
+ }
+ else if (parent instanceof PsiReferenceExpression) {
+ final PsiElement pparent = parent.getParent();
+ if (pparent instanceof PsiCallExpression) {
+ parent = pparent;
+ }
+ if (allowMethodCalls || !DebuggerUtils.hasSideEffects(parent)) {
+ expression = parent;
+ }
+ }
+ else if (parent instanceof PsiThisExpression) {
+ expression = parent;
+ }
+
+ if (expression != null) {
+ try {
+ PsiElement context = element;
+ if(parent instanceof PsiParameter) {
+ try {
+ context = ((PsiMethod)((PsiParameter)parent).getDeclarationScope()).getBody();
+ }
+ catch (Throwable ignored) {
+ }
+ }
+ else {
+ while(context != null && !(context instanceof PsiStatement) && !(context instanceof PsiClass)) {
+ context = context.getParent();
+ }
+ }
+ TextRange textRange = expression.getTextRange();
+ PsiElement psiExpression = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory().createExpressionFromText(expression.getText(), context);
+ return Pair.create(psiExpression, textRange);
+ }
+ catch (IncorrectOperationException e) {
+ LOG.debug(e);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/MultiProcessCommand.java b/java/debugger/impl/src/com/intellij/debugger/impl/MultiProcessCommand.java
new file mode 100644
index 0000000..db146c9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/MultiProcessCommand.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.openapi.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MultiProcessCommand implements Runnable{
+ private final List<Pair<DebugProcessImpl, DebuggerCommandImpl>> myCommands = new ArrayList<Pair<DebugProcessImpl, DebuggerCommandImpl>>();
+
+ public void run() {
+ while(true) {
+ Pair<DebugProcessImpl, DebuggerCommandImpl> pair;
+ synchronized(myCommands) {
+ if(myCommands.isEmpty()) {
+ break;
+ }
+ pair = myCommands.remove(0);
+ }
+ pair.getFirst().getManagerThread().invokeAndWait(pair.getSecond());
+ }
+ }
+
+ public void cancel() {
+ synchronized(myCommands) {
+ myCommands.clear();
+ }
+ }
+
+ public boolean isEmpty() {
+ return myCommands.isEmpty();
+ }
+
+ public void addCommand(DebugProcessImpl debugProcess, DebuggerCommandImpl command) {
+ myCommands.add(new Pair<DebugProcessImpl, DebuggerCommandImpl>(debugProcess, command));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java b/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java
new file mode 100644
index 0000000..2b23f37
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.text.CharArrayUtil;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * User: lex
+ * Date: Oct 29, 2003
+ * Time: 9:29:36 PM
+ */
+public class PositionUtil extends ContextUtil {
+ public static SourcePosition getSourcePosition(final StackFrameContext context) {
+ if(context instanceof DebuggerContextImpl) return ((DebuggerContextImpl)context).getSourcePosition();
+
+ return ContextUtil.getSourcePosition(context);
+ }
+
+ public static PsiElement getContextElement(final StackFrameContext context) {
+ if(context instanceof DebuggerContextImpl) return ((DebuggerContextImpl) context).getContextElement();
+
+ return ContextUtil.getContextElement(context);
+ }
+
+ @Nullable
+ public static <T extends PsiElement> T getPsiElementAt(final Project project, final Class<T> expectedPsiElementClass, final SourcePosition sourcePosition) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<T>() {
+ public T compute() {
+ final PsiFile psiFile = sourcePosition.getFile();
+ final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
+ if(document == null) {
+ return null;
+ }
+ final int spOffset = sourcePosition.getOffset();
+ if (spOffset < 0) {
+ return null;
+ }
+ final int offset = CharArrayUtil.shiftForward(document.getCharsSequence(), spOffset, " \t");
+ return PsiTreeUtil.getParentOfType(psiFile.findElementAt(offset), expectedPsiElementClass, false);
+ }
+ });
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/PrioritizedTask.java b/java/debugger/impl/src/com/intellij/debugger/impl/PrioritizedTask.java
new file mode 100644
index 0000000..5848088
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/PrioritizedTask.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 30, 2009
+ */
+public interface PrioritizedTask {
+ enum Priority {
+ HIGH, NORMAL, LOW
+ }
+
+ Priority getPriority();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java b/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java
new file mode 100644
index 0000000..d8d8dd4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.ui.MessageCategory;
+import com.intellij.util.ui.UIUtil;
+import com.sun.jdi.ReferenceType;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author lex
+ */
+class ReloadClassesWorker {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.ReloadClassesWorker");
+ private final DebuggerSession myDebuggerSession;
+ private final HotSwapProgress myProgress;
+
+ public ReloadClassesWorker(DebuggerSession session, HotSwapProgress progress) {
+ myDebuggerSession = session;
+ myProgress = progress;
+ }
+
+ private DebugProcessImpl getDebugProcess() {
+ return myDebuggerSession.getProcess();
+ }
+
+ private void processException(Throwable e) {
+ if (e.getMessage() != null) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, e.getMessage());
+ }
+
+ if (e instanceof ProcessCanceledException) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.INFORMATION, DebuggerBundle.message("error.operation.canceled"));
+ return;
+ }
+
+ if (e instanceof UnsupportedOperationException) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, DebuggerBundle.message("error.operation.not.supported.by.vm"));
+ }
+ else if (e instanceof NoClassDefFoundError) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, DebuggerBundle.message("error.class.def.not.found", e.getLocalizedMessage()));
+ }
+ else if (e instanceof VerifyError) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, DebuggerBundle.message("error.verification.error", e.getLocalizedMessage()));
+ }
+ else if (e instanceof UnsupportedClassVersionError) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, DebuggerBundle.message("error.unsupported.class.version", e.getLocalizedMessage()));
+ }
+ else if (e instanceof ClassFormatError) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, DebuggerBundle.message("error.class.format.error", e.getLocalizedMessage()));
+ }
+ else if (e instanceof ClassCircularityError) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, DebuggerBundle.message("error.class.circularity.error", e.getLocalizedMessage()));
+ }
+ else {
+ myProgress.addMessage(
+ myDebuggerSession, MessageCategory.ERROR,
+ DebuggerBundle.message("error.exception.while.reloading", e.getClass().getName(), e.getLocalizedMessage())
+ );
+ }
+ }
+
+ public void reloadClasses(final Map<String, HotSwapFile> modifiedClasses) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ if(modifiedClasses == null || modifiedClasses.size() == 0) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.INFORMATION, DebuggerBundle.message("status.hotswap.loaded.classes.up.to.date"));
+ return;
+ }
+
+ final DebugProcessImpl debugProcess = getDebugProcess();
+ final VirtualMachineProxyImpl virtualMachineProxy = debugProcess.getVirtualMachineProxy();
+ if(virtualMachineProxy == null) {
+ return;
+ }
+
+ final Project project = debugProcess.getProject();
+ final BreakpointManager breakpointManager = (DebuggerManagerEx.getInstanceEx(project)).getBreakpointManager();
+ breakpointManager.disableBreakpoints(debugProcess);
+
+ //virtualMachineProxy.suspend();
+
+ try {
+ RedefineProcessor redefineProcessor = new RedefineProcessor(virtualMachineProxy);
+
+ int processedClassesCount = 0;
+ for (final String qualifiedName : modifiedClasses.keySet()) {
+ processedClassesCount++;
+ if (qualifiedName != null) {
+ myProgress.setText(qualifiedName);
+ myProgress.setFraction(processedClassesCount / (double)modifiedClasses.size());
+ }
+ final HotSwapFile fileDescr = modifiedClasses.get(qualifiedName);
+ final byte[] content;
+ try {
+ content = FileUtil.loadFileBytes(fileDescr.file);
+ }
+ catch (IOException e) {
+ reportProblem(qualifiedName, e);
+ continue;
+ }
+ redefineProcessor.processClass(qualifiedName, content);
+ }
+ redefineProcessor.processPending();
+ myProgress.setFraction(1);
+
+ final int partiallyRedefinedClassesCount = redefineProcessor.getPartiallyRedefinedClassesCount();
+ if (partiallyRedefinedClassesCount == 0) {
+ myProgress.addMessage(myDebuggerSession, MessageCategory.INFORMATION,
+ DebuggerBundle.message("status.classes.reloaded", redefineProcessor.getProcessedClassesCount()));
+ }
+ else {
+ final String message = DebuggerBundle.message("status.classes.not.all.versions.reloaded", partiallyRedefinedClassesCount,
+ redefineProcessor.getProcessedClassesCount());
+ myProgress.addMessage(myDebuggerSession, MessageCategory.WARNING, message);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("classes reloaded");
+ }
+ }
+ catch (Throwable e) {
+ processException(e);
+ }
+
+ //noinspection SSBasedInspection
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ public void run() {
+ if (project.isDisposed()) {
+ return;
+ }
+ final BreakpointManager breakpointManager = (DebuggerManagerEx.getInstanceEx(project)).getBreakpointManager();
+ breakpointManager.reloadBreakpoints();
+ debugProcess.getRequestsManager().clearWarnings();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("requests updated");
+ LOG.debug("time stamp set");
+ }
+ myDebuggerSession.refresh(false);
+
+ /*
+ debugProcess.getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ try {
+ breakpointManager.enableBreakpoints(debugProcess);
+ }
+ catch (Exception e) {
+ processException(e);
+ }
+ //try {
+ // virtualMachineProxy.resume();
+ //}
+ //catch (Exception e) {
+ // processException(e);
+ //}
+ }
+
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+ });
+ */
+ }
+ });
+ try {
+ breakpointManager.enableBreakpoints(debugProcess);
+ }
+ catch (Exception e) {
+ processException(e);
+ }
+ }
+
+ private void reportProblem(final String qualifiedName, @Nullable Exception ex) {
+ String reason = null;
+ if (ex != null) {
+ reason = ex.getLocalizedMessage();
+ }
+ if (reason == null || reason.length() == 0) {
+ reason = DebuggerBundle.message("error.io.error");
+ }
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ buf.append(qualifiedName).append(" : ").append(reason);
+ myProgress.addMessage(myDebuggerSession, MessageCategory.ERROR, buf.toString());
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ private static class RedefineProcessor {
+ /**
+ * number of classes that will be reloaded in one go.
+ * Such restriction is needed to deal with big number of classes being reloaded
+ */
+ private static final int CLASSES_CHUNK_SIZE = 100;
+ private final VirtualMachineProxyImpl myVirtualMachineProxy;
+ private final Map<ReferenceType, byte[]> myRedefineMap = new HashMap<ReferenceType, byte[]>();
+ private int myProcessedClassesCount;
+ private int myPartiallyRedefinedClassesCount;
+
+ public RedefineProcessor(VirtualMachineProxyImpl virtualMachineProxy) {
+ myVirtualMachineProxy = virtualMachineProxy;
+ }
+
+ public void processClass(String qualifiedName, byte[] content) throws Throwable {
+ final List<ReferenceType> vmClasses = myVirtualMachineProxy.classesByName(qualifiedName);
+ if (vmClasses.isEmpty()) return;
+
+ if (vmClasses.size() == 1) {
+ myRedefineMap.put(vmClasses.get(0), content);
+ if (myRedefineMap.size() >= CLASSES_CHUNK_SIZE) {
+ processChunk();
+ }
+ return;
+ }
+
+ int redefinedVersionsCount = 0;
+ Throwable error = null;
+ for (ReferenceType vmClass : vmClasses) {
+ try {
+ myVirtualMachineProxy.redefineClasses(Collections.singletonMap(vmClass, content));
+ redefinedVersionsCount++;
+ }
+ catch (Throwable t) {
+ error = t;
+ }
+ }
+ if (redefinedVersionsCount == 0) {
+ throw error;
+ }
+
+ if (redefinedVersionsCount < vmClasses.size()) {
+ myPartiallyRedefinedClassesCount++;
+ }
+ myProcessedClassesCount++;
+ }
+
+ private void processChunk() throws Throwable {
+ // reload this portion of classes and clear the map to free memory
+ try {
+ myVirtualMachineProxy.redefineClasses(myRedefineMap);
+ myProcessedClassesCount += myRedefineMap.size();
+ }
+ finally {
+ myRedefineMap.clear();
+ }
+ }
+
+ public void processPending() throws Throwable {
+ if (myRedefineMap.size() > 0) {
+ processChunk();
+ }
+ }
+
+ public int getProcessedClassesCount() {
+ return myProcessedClassesCount;
+ }
+
+ public int getPartiallyRedefinedClassesCount() {
+ return myPartiallyRedefinedClassesCount;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java b/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java
new file mode 100644
index 0000000..207c5be
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 30, 2004
+ */
+public final class SimpleStackFrameContext implements StackFrameContext{
+ private final StackFrameProxy myProxy;
+ private final DebugProcess myProcess;
+
+ public SimpleStackFrameContext(StackFrameProxy proxy, DebugProcess process) {
+ myProxy = proxy;
+ myProcess = process;
+ }
+
+ public StackFrameProxy getFrameProxy() {
+ return myProxy;
+ }
+
+ public DebugProcess getDebugProcess() {
+ return myProcess;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java
new file mode 100644
index 0000000..79038ce
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.ArgumentValueDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Value;
+
+public class ArgValueData extends DescriptorData<ArgumentValueDescriptorImpl>{
+ private final int myIndex;
+ private final Value myValue;
+
+ public ArgValueData(int index, Value value) {
+ super();
+ myIndex = index;
+ myValue = value;
+ }
+
+ protected ArgumentValueDescriptorImpl createDescriptorImpl(Project project) {
+ return new ArgumentValueDescriptorImpl(project, myIndex, myValue);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof ArgValueData)) return false;
+
+ return myIndex == ((ArgValueData)object).myIndex;
+ }
+
+ public int hashCode() {
+ return myIndex;
+ }
+
+ public DisplayKey<ArgumentValueDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<ArgumentValueDescriptorImpl>(myIndex);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java
new file mode 100644
index 0000000..7973da6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ArrayReference;
+import org.jetbrains.annotations.NotNull;
+
+public final class ArrayItemData extends DescriptorData<ArrayElementDescriptorImpl>{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.descriptors.data.ArrayItemData");
+
+ private final ArrayReference myArray;
+ private final int myIndex;
+
+ public ArrayItemData(@NotNull ArrayReference arrRef, int idx) {
+ LOG.assertTrue(0 <= idx);
+ if(LOG.isDebugEnabled()) {
+ LOG.assertTrue(idx <= arrRef.length());
+ }
+ myArray = arrRef;
+ myIndex = idx;
+ }
+
+ protected ArrayElementDescriptorImpl createDescriptorImpl(Project project) {
+ return new ArrayElementDescriptorImpl(project, myArray, myIndex);
+ }
+
+ public DisplayKey<ArrayElementDescriptorImpl> getDisplayKey() {
+ return new ArrayItemDisplayKeyImpl(myIndex);
+ }
+
+ public boolean equals(Object object) {
+ return object instanceof ArrayItemData && myArray.equals(((ArrayItemData)object).myArray) && ((ArrayItemData)object).myIndex == myIndex;
+ }
+
+ public int hashCode() {
+ return myArray.hashCode() + myIndex;
+ }
+
+ private static class ArrayItemDisplayKeyImpl implements DisplayKey<ArrayElementDescriptorImpl> {
+ private final int myIndex;
+
+ public ArrayItemDisplayKeyImpl(int index) {
+ myIndex = index;
+ }
+
+ public boolean equals(Object o) {
+ return o instanceof ArrayItemDisplayKeyImpl && ((ArrayItemDisplayKeyImpl)o).myIndex == myIndex;
+ }
+
+ public int hashCode() {
+ return 0;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DescriptorData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DescriptorData.java
new file mode 100644
index 0000000..41f9181
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DescriptorData.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+
+public abstract class DescriptorData <T extends NodeDescriptor> implements DescriptorKey<T>{
+ private static final Key DESCRIPTOR_DATA = new Key("DESCRIPTOR_DATA");
+
+ protected DescriptorData() {
+ }
+
+ public T createDescriptor(Project project) {
+ T descriptor = createDescriptorImpl(project);
+ descriptor.putUserData(DESCRIPTOR_DATA, this);
+ return descriptor;
+ }
+
+ protected abstract T createDescriptorImpl(Project project);
+
+ public abstract boolean equals(Object object);
+
+ public abstract int hashCode();
+
+ public abstract DisplayKey<T> getDisplayKey();
+
+ public static <T extends NodeDescriptor> DescriptorData<T> getDescriptorData(T descriptor) {
+ return (DescriptorData<T>)descriptor.getUserData(DESCRIPTOR_DATA);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DescriptorKey.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DescriptorKey.java
new file mode 100644
index 0000000..52fcaec
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DescriptorKey.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+
+public interface DescriptorKey <T extends NodeDescriptor>{
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DisplayKey.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DisplayKey.java
new file mode 100644
index 0000000..32589f4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/DisplayKey.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+
+public interface DisplayKey <T extends NodeDescriptor> extends DescriptorKey<T>{
+ boolean equals(Object object);
+
+ int hashCode();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java
new file mode 100644
index 0000000..8392489
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Field;
+import com.sun.jdi.ObjectReference;
+import org.jetbrains.annotations.NotNull;
+
+public final class FieldData extends DescriptorData<FieldDescriptorImpl>{
+ private final ObjectReference myObjRef;
+ private final Field myField;
+
+ public FieldData(@NotNull ObjectReference objRef, @NotNull Field field) {
+ myObjRef = objRef;
+ myField = field;
+ }
+
+ protected FieldDescriptorImpl createDescriptorImpl(Project project) {
+ return new FieldDescriptorImpl(project, myObjRef, myField);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof FieldData)) {
+ return false;
+ }
+ final FieldData fieldData = (FieldData)object;
+ return fieldData.myField == myField && fieldData.myObjRef.equals(myObjRef);
+ }
+
+ public int hashCode() {
+ return myObjRef.hashCode() + myField.hashCode();
+ }
+
+ public DisplayKey<FieldDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<FieldDescriptorImpl>(myField);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java
new file mode 100644
index 0000000..3e401a0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.ui.impl.watch.LocalVariableDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.StringBuilderSpinAllocator;
+
+public class LocalData extends DescriptorData<LocalVariableDescriptorImpl>{
+ private final LocalVariableProxyImpl myLocalVariable;
+
+ public LocalData(LocalVariableProxyImpl localVariable) {
+ super();
+ myLocalVariable = localVariable;
+ }
+
+ protected LocalVariableDescriptorImpl createDescriptorImpl(Project project) {
+ return new LocalVariableDescriptorImpl(project, myLocalVariable);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof LocalData)) return false;
+
+ return ((LocalData)object).myLocalVariable.equals(myLocalVariable);
+ }
+
+ public int hashCode() {
+ return myLocalVariable.hashCode();
+ }
+
+ public DisplayKey<LocalVariableDescriptorImpl> getDisplayKey() {
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ return new SimpleDisplayKey<LocalVariableDescriptorImpl>(builder.append(myLocalVariable.typeName()).append("#").append(myLocalVariable.name()).toString());
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java
new file mode 100644
index 0000000..1139b2f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.MethodReturnValueDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.sun.jdi.Method;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public final class MethodReturnValueData extends DescriptorData<MethodReturnValueDescriptorImpl>{
+ private final @Nullable Value myReturnValue;
+ private final @NotNull Method myMethod;
+
+ public MethodReturnValueData(@NotNull Method method, @Nullable Value returnValue) {
+ super();
+ myMethod = method;
+ myReturnValue = returnValue;
+ }
+
+ public @Nullable Value getReturnValue() {
+ return myReturnValue;
+ }
+
+ public @NotNull Method getMethod() {
+ return myMethod;
+ }
+
+ protected MethodReturnValueDescriptorImpl createDescriptorImpl(Project project) {
+ return new MethodReturnValueDescriptorImpl(project, myMethod, myReturnValue);
+ }
+
+
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final MethodReturnValueData that = (MethodReturnValueData)o;
+
+ if (!myMethod.equals(that.myMethod)) return false;
+ if (myReturnValue != null ? !myReturnValue.equals(that.myReturnValue) : that.myReturnValue != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (myReturnValue != null ? myReturnValue.hashCode() : 0);
+ result = 31 * result + myMethod.hashCode();
+ return result;
+ }
+
+ public DisplayKey<MethodReturnValueDescriptorImpl> getDisplayKey() {
+ return new MethodReturnValueDisplayKey(myMethod, myReturnValue);
+ }
+
+ private static final class MethodReturnValueDisplayKey extends Pair<Method, Value> implements DisplayKey<MethodReturnValueDescriptorImpl> {
+ public MethodReturnValueDisplayKey(@NotNull Method method, @Nullable Value value) {
+ super(method, value);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/SimpleDisplayKey.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/SimpleDisplayKey.java
new file mode 100644
index 0000000..3762611
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/SimpleDisplayKey.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+
+public class SimpleDisplayKey<T extends NodeDescriptor> implements DisplayKey<T>{
+ private final Object myKey;
+
+ public SimpleDisplayKey(Object key) {
+ myKey = key;
+ }
+
+ public boolean equals(Object o) {
+ if(!(o instanceof SimpleDisplayKey)) return false;
+ return ((SimpleDisplayKey)o).myKey.equals(myKey);
+ }
+
+ public int hashCode() {
+ return myKey.hashCode();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StackFrameData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StackFrameData.java
new file mode 100644
index 0000000..6dc8400
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StackFrameData.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.ui.impl.watch.MethodsTracker;
+import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+
+public class StackFrameData extends DescriptorData<StackFrameDescriptorImpl>{
+ private final StackFrameProxyImpl myFrame;
+ private final FrameDisplayKey myDisplayKey;
+ private final MethodsTracker myMethodsTracker;
+
+ public StackFrameData(StackFrameProxyImpl frame) {
+ super();
+ myFrame = frame;
+ myDisplayKey = new FrameDisplayKey(NodeManagerImpl.getContextKeyForFrame(frame));
+ myMethodsTracker = new MethodsTracker();
+
+ }
+
+ protected StackFrameDescriptorImpl createDescriptorImpl(Project project) {
+ return new StackFrameDescriptorImpl(myFrame, myMethodsTracker);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof StackFrameData)) return false;
+
+ return ((StackFrameData)object).myFrame == myFrame;
+ }
+
+ public int hashCode() {
+ return myFrame.hashCode();
+ }
+
+ public DisplayKey<StackFrameDescriptorImpl> getDisplayKey() {
+ return myDisplayKey;
+ }
+
+ private static class FrameDisplayKey implements DisplayKey<StackFrameDescriptorImpl>{
+ private final String myContextKey;
+
+ public FrameDisplayKey(final String contextKey) {
+ myContextKey = contextKey;
+ }
+
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final FrameDisplayKey that = (FrameDisplayKey)o;
+
+ if (!Comparing.equal(myContextKey, that.myContextKey)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return myContextKey == null? 0 : myContextKey.hashCode();
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java
new file mode 100644
index 0000000..178954b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.StaticDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.sun.jdi.ReferenceType;
+import org.jetbrains.annotations.NotNull;
+
+public final class StaticData extends DescriptorData<StaticDescriptorImpl>{
+ private static final Key STATIC = new Key("STATIC");
+
+ private final ReferenceType myRefType;
+
+ public StaticData(@NotNull ReferenceType refType) {
+ myRefType = refType;
+ }
+
+ public ReferenceType getRefType() {
+ return myRefType;
+ }
+
+ protected StaticDescriptorImpl createDescriptorImpl(Project project) {
+ return new StaticDescriptorImpl(myRefType);
+ }
+
+ public boolean equals(Object object) {
+ return object instanceof StaticData;
+
+ }
+
+ public int hashCode() {
+ return STATIC.hashCode();
+ }
+
+ public DisplayKey<StaticDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<StaticDescriptorImpl>(STATIC);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java
new file mode 100644
index 0000000..adfbd3a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Field;
+import org.jetbrains.annotations.NotNull;
+
+public final class StaticFieldData extends DescriptorData<FieldDescriptorImpl>{
+ private final Field myField;
+
+ public StaticFieldData(@NotNull Field field) {
+ myField = field;
+ }
+
+ protected FieldDescriptorImpl createDescriptorImpl(Project project) {
+ return new FieldDescriptorImpl(project, null, myField);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof StaticFieldData)) {
+ return false;
+ }
+ final StaticFieldData fieldData = (StaticFieldData)object;
+ return (fieldData.myField == myField);
+ }
+
+ public int hashCode() {
+ return myField.hashCode();
+ }
+
+ public DisplayKey<FieldDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<FieldDescriptorImpl>(myField);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java
new file mode 100644
index 0000000..9a7afd6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.ui.impl.watch.ThisDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+
+public final class ThisData extends DescriptorData<ThisDescriptorImpl>{
+
+ private static final Key THIS = new Key("THIS");
+
+ protected ThisDescriptorImpl createDescriptorImpl(Project project) {
+ return new ThisDescriptorImpl(project);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof ThisData)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return THIS.hashCode();
+ }
+
+ public DisplayKey<ThisDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<ThisDescriptorImpl>(THIS);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java
new file mode 100644
index 0000000..543ce53
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.openapi.project.Project;
+
+public class ThreadData extends DescriptorData<ThreadDescriptorImpl> {
+ private final ThreadReferenceProxyImpl myThread;
+ public ThreadData(ThreadReferenceProxyImpl thread) {
+ super();
+ myThread = thread;
+ }
+
+ protected ThreadDescriptorImpl createDescriptorImpl(Project project) {
+ return new ThreadDescriptorImpl(myThread);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof ThreadData)) {
+ return false;
+ }
+ return myThread.equals(((ThreadData)object).myThread);
+ }
+
+ public int hashCode() {
+ return myThread.hashCode();
+ }
+
+ public DisplayKey<ThreadDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<ThreadDescriptorImpl>(myThread);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java
new file mode 100644
index 0000000..fef9333
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadGroupDescriptorImpl;
+import com.intellij.openapi.project.Project;
+
+public class ThreadGroupData extends DescriptorData<ThreadGroupDescriptorImpl>{
+ private final ThreadGroupReferenceProxyImpl myThreadGroup;
+
+ public ThreadGroupData(ThreadGroupReferenceProxyImpl threadGroup) {
+ super();
+ myThreadGroup = threadGroup;
+ }
+
+ protected ThreadGroupDescriptorImpl createDescriptorImpl(Project project) {
+ return new ThreadGroupDescriptorImpl(myThreadGroup);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof ThreadGroupData)) return false;
+
+ return myThreadGroup.equals(((ThreadGroupData)object).myThreadGroup);
+ }
+
+ public int hashCode() {
+ return myThreadGroup.hashCode();
+ }
+
+ public DisplayKey<ThreadGroupDescriptorImpl> getDisplayKey() {
+ return new SimpleDisplayKey<ThreadGroupDescriptorImpl>(myThreadGroup);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/UserExpressionData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/UserExpressionData.java
new file mode 100644
index 0000000..5553d3b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/UserExpressionData.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.ui.impl.watch.UserExpressionDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.UserExpressionDescriptor;
+import com.intellij.openapi.project.Project;
+
+public class UserExpressionData extends DescriptorData<UserExpressionDescriptor>{
+ private final ValueDescriptorImpl myParentDescriptor;
+ private final String myTypeName;
+ private final String myName;
+ protected TextWithImports myText;
+
+ public UserExpressionData(ValueDescriptorImpl parentDescriptor, String typeName, String name, TextWithImports text) {
+ super();
+ myParentDescriptor = parentDescriptor;
+ myTypeName = typeName;
+ myName = name;
+ myText = text;
+ }
+
+ protected UserExpressionDescriptorImpl createDescriptorImpl(Project project) {
+ return new UserExpressionDescriptorImpl(project, myParentDescriptor, myTypeName, myName, myText);
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof UserExpressionData)) return false;
+
+ return myName.equals(((UserExpressionData)object).myName);
+ }
+
+ public int hashCode() {
+ return myName.hashCode();
+ }
+
+ public DisplayKey<UserExpressionDescriptor> getDisplayKey() {
+ return new SimpleDisplayKey<UserExpressionDescriptor>(myTypeName + myName);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java
new file mode 100644
index 0000000..4b44a0d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl.descriptors.data;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 30, 2007
+ */
+public final class WatchItemData extends DescriptorData<WatchItemDescriptor>{
+ private final TextWithImports myText;
+ private final Value myValue;
+
+ public WatchItemData(TextWithImports text, @Nullable Value value) {
+ myText = text;
+ myValue = value;
+ }
+
+ protected WatchItemDescriptor createDescriptorImpl(final Project project) {
+ return new WatchItemDescriptor(project, myText, myValue);
+ }
+
+ public boolean equals(final Object object) {
+ if (object instanceof WatchItemData) {
+ return myText.equals(((WatchItemData)object).myText);
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return myText.hashCode();
+ }
+
+ public DisplayKey<WatchItemDescriptor> getDisplayKey() {
+ return new SimpleDisplayKey<WatchItemDescriptor>(myText.getText());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/JdiProxy.java b/java/debugger/impl/src/com/intellij/debugger/jdi/JdiProxy.java
new file mode 100644
index 0000000..ab8a3ce
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/JdiProxy.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.jdi;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jul 10, 2003
+ * Time: 4:53:44 PM
+ * To change this template use Options | File Templates.
+ */
+public abstract class JdiProxy {
+ protected JdiTimer myTimer;
+ private int myTimeStamp = 0;
+
+ public JdiProxy(JdiTimer timer) {
+ myTimer = timer;
+ myTimeStamp = myTimer.getCurrentTime();
+ }
+
+ protected void checkValid() {
+ if(!isValid()) {
+ myTimeStamp = myTimer.getCurrentTime();
+ clearCaches();
+ }
+ }
+
+ /**
+ * @deprecated for testing only
+ * @return
+ */
+ public boolean isValid() {
+ return myTimeStamp == myTimer.getCurrentTime();
+ }
+
+ protected abstract void clearCaches();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/JdiTimer.java b/java/debugger/impl/src/com/intellij/debugger/jdi/JdiTimer.java
new file mode 100644
index 0000000..2bef610
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/JdiTimer.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.jdi;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jul 10, 2003
+ * Time: 5:13:25 PM
+ * To change this template use Options | File Templates.
+ */
+interface JdiTimer {
+ int getCurrentTime();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java
new file mode 100644
index 0000000..0ccda00
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.jdi;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.jdi.LocalVariableProxy;
+import com.intellij.openapi.util.Comparing;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.LocalVariable;
+import com.sun.jdi.Type;
+
+public class LocalVariableProxyImpl extends JdiProxy implements LocalVariableProxy {
+ private final StackFrameProxyImpl myFrame;
+ private final String myVariableName;
+ private final String myTypeName;
+
+ private LocalVariable myVariable;
+ private Type myVariableType;
+
+ public LocalVariableProxyImpl(StackFrameProxyImpl frame, LocalVariable variable) {
+ super(frame.myTimer);
+ myFrame = frame;
+ myVariableName = variable.name();
+ myTypeName = variable.typeName();
+ myVariable = variable;
+ }
+
+ protected void clearCaches() {
+ myVariable = null;
+ myVariableType = null;
+ }
+
+ public LocalVariable getVariable() throws EvaluateException {
+ checkValid();
+ if(myVariable == null) {
+ myVariable = myFrame.visibleVariableByNameInt(myVariableName);
+
+ if(myVariable == null) {
+ //myFrame is not this variable's frame
+ throw EvaluateExceptionUtil.createEvaluateException(new IncompatibleThreadStateException());
+ }
+ }
+
+ return myVariable;
+ }
+
+ public Type getType() throws EvaluateException, ClassNotLoadedException {
+ if (myVariableType == null) {
+ myVariableType = getVariable().type();
+ }
+ return myVariableType;
+ }
+
+ public StackFrameProxyImpl getFrame() {
+ return myFrame;
+ }
+
+ public int hashCode() {
+ return 31 * myFrame.hashCode() + myVariableName.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if(o instanceof LocalVariableProxyImpl) {
+ LocalVariableProxyImpl proxy = (LocalVariableProxyImpl)o;
+ return Comparing.equal(proxy.myFrame, myFrame) && myVariableName.equals(proxy.myVariableName);
+ }
+ return false;
+ }
+
+ public String name() {
+ return myVariableName;
+ }
+
+ public String typeName() {
+ return myTypeName;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java
new file mode 100644
index 0000000..4e39599
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.jdi;
+
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ObjectReferenceProxyImpl extends JdiProxy {
+ private final ObjectReference myObjectReference;
+
+ //caches
+ private ReferenceType myReferenceType;
+ private Type myType;
+ private Boolean myIsCollected = null;
+
+ public ObjectReferenceProxyImpl(VirtualMachineProxyImpl virtualMachineProxy, @NotNull ObjectReference objectReference) {
+ super(virtualMachineProxy);
+ myObjectReference = objectReference;
+ }
+
+ public ObjectReference getObjectReference() {
+ checkValid();
+ return myObjectReference;
+ }
+
+ public VirtualMachineProxyImpl getVirtualMachineProxy() {
+ return (VirtualMachineProxyImpl) myTimer;
+ }
+
+ public ReferenceType referenceType() {
+ checkValid();
+ if (myReferenceType == null) {
+ myReferenceType = getObjectReference().referenceType();
+ }
+ return myReferenceType;
+ }
+
+ public Type type() {
+ checkValid();
+ if (myType == null) {
+ myType = getObjectReference().type();
+ }
+ return myType;
+ }
+
+ @NonNls
+ public String toString() {
+ final ObjectReference objectReference = getObjectReference();
+ //noinspection HardCodedStringLiteral
+ final String objRefString = objectReference != null? objectReference.toString() : "[referenced object collected]";
+ return "ObjectReferenceProxyImpl: " + objRefString + " " + super.toString();
+ }
+
+ public Map<Field, Value> getValues(List<? extends Field> list) {
+ return getObjectReference().getValues(list);
+ }
+
+ public void setValue(Field field, Value value) throws InvalidTypeException, ClassNotLoadedException {
+ getObjectReference().setValue(field, value);
+ }
+
+ public boolean isCollected() {
+ checkValid();
+ if (myIsCollected == null || Boolean.FALSE.equals(myIsCollected)) {
+ try {
+ myIsCollected = Boolean.valueOf(VirtualMachineProxyImpl.isCollected(myObjectReference));
+ }
+ catch (VMDisconnectedException e) {
+ myIsCollected = Boolean.TRUE;
+ }
+ }
+ return myIsCollected.booleanValue();
+ }
+
+ public long uniqueID() {
+ return getObjectReference().uniqueID();
+ }
+
+ /**
+ * @return a list of waiting ThreadReferenceProxies
+ * @throws IncompatibleThreadStateException
+ */
+ public List<ThreadReferenceProxyImpl> waitingThreads() throws IncompatibleThreadStateException {
+ List<ThreadReference> list = getObjectReference().waitingThreads();
+ List<ThreadReferenceProxyImpl> proxiesList = new ArrayList<ThreadReferenceProxyImpl>(list.size());
+
+ for (ThreadReference threadReference : list) {
+ proxiesList.add(getVirtualMachineProxy().getThreadReferenceProxy(threadReference));
+ }
+ return proxiesList;
+ }
+
+ public ThreadReferenceProxyImpl owningThread() throws IncompatibleThreadStateException {
+ ThreadReference threadReference = getObjectReference().owningThread();
+ return getVirtualMachineProxy().getThreadReferenceProxy(threadReference);
+ }
+
+ public int entryCount() throws IncompatibleThreadStateException {
+ return getObjectReference().entryCount();
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof ObjectReferenceProxyImpl)) {
+ return false;
+ }
+ if(this == o) return true;
+
+ ObjectReference ref = myObjectReference;
+ return ref != null && ref.equals(((ObjectReferenceProxyImpl)o).myObjectReference);
+ }
+
+
+ public int hashCode() {
+ return myObjectReference.hashCode();
+ }
+
+ /**
+ * The advice to the proxy to clear cached data.
+ */
+ protected void clearCaches() {
+ if (Boolean.FALSE.equals(myIsCollected)) {
+ // clearing cache makes sence only if the object has not been collected yet
+ myIsCollected = null;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java
new file mode 100644
index 0000000..01a6c14
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java
@@ -0,0 +1,366 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.jdi;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+public class StackFrameProxyImpl extends JdiProxy implements StackFrameProxy {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.StackFrameProxyImpl");
+ private final ThreadReferenceProxyImpl myThreadProxy;
+ private final int myFrameFromBottomIndex; // 1-based
+
+ //caches
+ private int myFrameIndex = -1;
+ private StackFrame myStackFrame;
+ private ObjectReference myThisReference;
+ private ClassLoaderReference myClassLoader;
+ private Boolean myIsObsolete = null;
+ private Map<LocalVariable,Value> myAllValues;
+
+ public StackFrameProxyImpl(ThreadReferenceProxyImpl threadProxy, @NotNull StackFrame frame, int fromBottomIndex /* 1-based */) {
+ super(threadProxy.getVirtualMachine());
+ myThreadProxy = threadProxy;
+ myFrameFromBottomIndex = fromBottomIndex;
+ myStackFrame = frame;
+ }
+
+ public boolean isObsolete() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ checkValid();
+ if (myIsObsolete == null) {
+ try {
+ boolean isObsolete = (getVirtualMachine().canRedefineClasses() && location().method().isObsolete());
+ //boolean isObsolete = (getVirtualMachine().versionHigher("1.4") && location().method().isObsolete());
+ myIsObsolete = isObsolete? Boolean.TRUE : Boolean.FALSE;
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return isObsolete();
+ }
+ catch (InternalException e) {
+ if (e.errorCode() == 23 /*INVALID_METHODID accoeding to JDI sources*/) {
+ myIsObsolete = Boolean.TRUE;
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+ return myIsObsolete.booleanValue();
+ }
+
+ protected void clearCaches() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ //DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("caches cleared " + super.toString());
+ }
+ myFrameIndex = -1;
+ myStackFrame = null;
+ myIsObsolete = null;
+ myThisReference = null;
+ myClassLoader = null;
+ myAllValues = null;
+ }
+
+ /**
+ * Use with caution. Better access stackframe data through the Proxy's methods
+ */
+
+ public StackFrame getStackFrame() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ checkValid();
+
+ if(myStackFrame == null) {
+ try {
+ final ThreadReference threadRef = myThreadProxy.getThreadReference();
+ myStackFrame = threadRef.frame(getFrameIndex());
+ }
+ catch (IndexOutOfBoundsException e) {
+ throw new EvaluateException(e.getMessage(), e);
+ }
+ catch (ObjectCollectedException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.thread.collected"));
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ return myStackFrame;
+ }
+
+ public int getFrameIndex() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ checkValid();
+ if(myFrameIndex == -1) {
+ int count = myThreadProxy.frameCount();
+
+ if(myFrameFromBottomIndex > count) {
+ throw EvaluateExceptionUtil.createEvaluateException(new IncompatibleThreadStateException());
+ }
+
+ myFrameIndex = count - myFrameFromBottomIndex;
+ }
+ return myFrameIndex;
+ }
+
+// public boolean isProxiedFrameValid() {
+// if (myStackFrame != null) {
+// try {
+// myStackFrame.thread();
+// return true;
+// }
+// catch (InvalidStackFrameException e) {
+// }
+// }
+// return false;
+// }
+
+ public VirtualMachineProxyImpl getVirtualMachine() {
+ return (VirtualMachineProxyImpl) myTimer;
+ }
+
+ public Location location() throws EvaluateException {
+ try {
+ return getStackFrame().location();
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return location();
+ }
+ }
+
+ public ThreadReferenceProxyImpl threadProxy() {
+ return myThreadProxy;
+ }
+
+ public @NonNls String toString() {
+ try {
+ return "StackFrameProxyImpl: " + getStackFrame().toString();
+ }
+ catch (EvaluateException e) {
+ return "StackFrameProxyImpl: " + e.getMessage() + "; frameFromBottom = " + myFrameFromBottomIndex + " threadName = " + threadProxy().name();
+ }
+ }
+
+ public ObjectReference thisObject() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ checkValid();
+ try {
+ if(myThisReference == null) {
+ myThisReference = getStackFrame().thisObject();
+ }
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return thisObject();
+ }
+ catch (InternalException e) {
+ // supress some internal errors caused by bugs in specific JDI implementations
+ if(e.errorCode() != 23) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+ return myThisReference;
+ }
+
+ public List<LocalVariableProxyImpl> visibleVariables() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ List<LocalVariable> list = getStackFrame().visibleVariables();
+
+ List<LocalVariableProxyImpl> locals = new ArrayList<LocalVariableProxyImpl>();
+ for (Iterator<LocalVariable> iterator = list.iterator(); iterator.hasNext();) {
+ LocalVariable localVariable = iterator.next();
+ LOG.assertTrue(localVariable != null);
+ locals.add(new LocalVariableProxyImpl(this, localVariable));
+ }
+ return locals;
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return visibleVariables();
+ }
+ catch (AbsentInformationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ public LocalVariableProxyImpl visibleVariableByName(String name) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final LocalVariable variable = visibleVariableByNameInt(name);
+ return variable != null ? new LocalVariableProxyImpl(this, variable) : null;
+ }
+
+ protected LocalVariable visibleVariableByNameInt(String name) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ try {
+ return getStackFrame().visibleVariableByName(name);
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return getStackFrame().visibleVariableByName(name);
+ }
+ }
+ catch (InvalidStackFrameException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (AbsentInformationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+
+ public Value getValue(LocalVariableProxyImpl localVariable) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ final Map<LocalVariable, Value> allValues = getAllValues();
+ return allValues.get(localVariable.getVariable());
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return getValue(localVariable);
+ }
+ }
+
+ public List<Value> getArgumentValues() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ final StackFrame stackFrame = getStackFrame();
+ if (stackFrame != null) {
+ return stackFrame.getArgumentValues();
+ }
+ return Collections.emptyList();
+ }
+ catch (InternalException e) {
+ // From Oracle's forums:
+ // This could be a JPDA bug. Unexpected JDWP Error: 32 means that an 'opaque' frame was detected at the lower JPDA levels,
+ // typically a native frame.
+ if (e.errorCode() == 32 /*opaque frame JDI bug*/ ) {
+ return Collections.emptyList();
+ }
+ else {
+ throw e;
+ }
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ return getArgumentValues();
+ }
+ }
+
+ private Map<LocalVariable, Value> getAllValues() throws EvaluateException{
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ checkValid();
+ if (myAllValues == null) {
+ try {
+ final StackFrame stackFrame = getStackFrame();
+ final Map<LocalVariable, Value> values = stackFrame.getValues(stackFrame.visibleVariables());
+ myAllValues = new HashMap<LocalVariable, Value>(values.size());
+ for (final LocalVariable variable : values.keySet()) {
+ final Value value = values.get(variable);
+ myAllValues.put(variable, value);
+ }
+ }
+ catch (InconsistentDebugInfoException e) {
+ clearCaches();
+ throw EvaluateExceptionUtil.INCONSISTEND_DEBUG_INFO;
+ }
+ catch (AbsentInformationException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ }
+ return myAllValues;
+ }
+
+ public void setValue(LocalVariableProxyImpl localVariable, Value value) throws EvaluateException,
+ ClassNotLoadedException,
+ InvalidTypeException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ final LocalVariable variable = localVariable.getVariable();
+ final StackFrame stackFrame = getStackFrame();
+ stackFrame.setValue(variable, (value instanceof ObjectReference)? ((ObjectReference)value) : value);
+ if (myAllValues != null) {
+ // update cached data if any
+ // re-read the value just set from the stackframe to be 100% sure
+ myAllValues.put(variable, stackFrame.getValue(variable));
+ }
+ }
+ catch (InvalidStackFrameException e) {
+ clearCaches();
+ setValue(localVariable, value);
+ }
+ }
+
+ public int hashCode() {
+ return 31 * myThreadProxy.hashCode() + myFrameFromBottomIndex;
+ }
+
+
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof StackFrameProxyImpl)) {
+ return false;
+ }
+ StackFrameProxyImpl frameProxy = (StackFrameProxyImpl)obj;
+ if(frameProxy == this)return true;
+
+ return (myFrameFromBottomIndex == frameProxy.myFrameFromBottomIndex) &&
+ (myThreadProxy.equals(frameProxy.myThreadProxy));
+ }
+
+ public boolean isLocalVariableVisible(LocalVariableProxyImpl var) throws EvaluateException {
+ try {
+ return var.getVariable().isVisible(getStackFrame());
+ }
+ catch (IllegalArgumentException e) {
+ // can be thrown if frame's method is different than variable's method
+ return false;
+ }
+ }
+
+ public ClassLoaderReference getClassLoader() throws EvaluateException {
+ if(myClassLoader == null) {
+ myClassLoader = location().declaringType().classLoader();
+ }
+ return myClassLoader;
+ }
+
+ public boolean isBottom() {
+ return myFrameFromBottomIndex == 1;
+ }
+
+ public int getIndexFromBottom() {
+ return myFrameFromBottomIndex;
+ }
+}
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/StringReferenceProxy.java b/java/debugger/impl/src/com/intellij/debugger/jdi/StringReferenceProxy.java
new file mode 100644
index 0000000..01aa8e5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/StringReferenceProxy.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.jdi;
+
+import com.sun.jdi.StringReference;
+
+public class StringReferenceProxy extends ObjectReferenceProxyImpl{
+ private String myStringValue;
+
+ public StringReferenceProxy(VirtualMachineProxyImpl virtualMachineProxy, StringReference objectReference) {
+ super(virtualMachineProxy, objectReference);
+ }
+
+ public StringReference getStringReference() {
+ return (StringReference)getObjectReference();
+ }
+
+ public String value() {
+ checkValid();
+ if (myStringValue == null) {
+ myStringValue = getStringReference().value();
+ }
+ return myStringValue;
+ }
+
+ public void clearCaches() {
+ myStringValue = null;
+ super.clearCaches();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadGroupReferenceProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadGroupReferenceProxyImpl.java
new file mode 100644
index 0000000..560d5a7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadGroupReferenceProxyImpl.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.jdi;
+
+import com.intellij.debugger.engine.jdi.ThreadGroupReferenceProxy;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.ThreadGroupReference;
+import com.sun.jdi.ThreadReference;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ThreadGroupReferenceProxyImpl extends ObjectReferenceProxyImpl implements ThreadGroupReferenceProxy{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl");
+ //caches
+ private ThreadGroupReferenceProxyImpl myParentThreadGroupProxy;
+ private boolean myIsParentGroupCached = false;
+ private String myName;
+
+ public ThreadGroupReferenceProxyImpl(VirtualMachineProxyImpl virtualMachineProxy, ThreadGroupReference threadGroupReference) {
+ super(virtualMachineProxy, threadGroupReference);
+ }
+
+ public ThreadGroupReference getThreadGroupReference() {
+ return (ThreadGroupReference)getObjectReference();
+ }
+
+ public String name() {
+ checkValid();
+ if (myName == null) {
+ myName = getThreadGroupReference().name();
+ }
+ return myName;
+ }
+
+ public ThreadGroupReferenceProxyImpl parent() {
+ checkValid();
+ if (!myIsParentGroupCached) {
+ myParentThreadGroupProxy = getVirtualMachineProxy().getThreadGroupReferenceProxy(getThreadGroupReference().parent());
+ myIsParentGroupCached = true;
+ }
+ return myParentThreadGroupProxy;
+ }
+
+ public @NonNls String toString() {
+ return "ThreadGroupReferenceProxy: " + getThreadGroupReference().toString();
+ }
+
+ public void suspend() {
+ getThreadGroupReference().suspend();
+ }
+
+ public void resume() {
+ getThreadGroupReference().resume();
+ }
+
+ public List<ThreadReferenceProxyImpl> threads() {
+ List<ThreadReference> list = getThreadGroupReference().threads();
+ List<ThreadReferenceProxyImpl> proxies = new ArrayList<ThreadReferenceProxyImpl>(list.size());
+
+ for (Iterator<ThreadReference> iterator = list.iterator(); iterator.hasNext();) {
+ ThreadReference threadReference = iterator.next();
+ proxies.add(getVirtualMachineProxy().getThreadReferenceProxy(threadReference));
+ }
+ return proxies;
+ }
+
+ public List<ThreadGroupReferenceProxyImpl> threadGroups() {
+ List<ThreadGroupReference> list = getThreadGroupReference().threadGroups();
+ List<ThreadGroupReferenceProxyImpl> proxies = new ArrayList<ThreadGroupReferenceProxyImpl>(list.size());
+
+ for (Iterator<ThreadGroupReference> iterator = list.iterator(); iterator.hasNext();) {
+ ThreadGroupReference threadGroupReference = iterator.next();
+ proxies.add(getVirtualMachineProxy().getThreadGroupReferenceProxy(threadGroupReference));
+ }
+ return proxies;
+ }
+
+ public void clearCaches() {
+// myIsParentGroupCached = false;
+// myName = null;
+// myParentThreadGroupProxy = null;
+ super.clearCaches();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java
new file mode 100644
index 0000000..25d08bb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.jdi;
+
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl implements ThreadReferenceProxy {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.ThreadReferenceProxyImpl");
+ // cached data
+ private String myName;
+ private int myFrameCount = -1;
+ // stackframes, 0 - bottom
+ private final List<StackFrameProxyImpl> myFramesFromBottom = new ArrayList<StackFrameProxyImpl>();
+ //cache build on the base of myFramesFromBottom 0 - top, initially nothing is cached
+ private List<StackFrameProxyImpl> myFrames = null;
+
+ private ThreadGroupReferenceProxyImpl myThreadGroupProxy;
+
+ public static Comparator<ThreadReferenceProxyImpl> ourComparator = new Comparator<ThreadReferenceProxyImpl>() {
+ public int compare(ThreadReferenceProxyImpl th1, ThreadReferenceProxyImpl th2) {
+ return th1.name().compareToIgnoreCase(th2.name());
+ }
+ };
+
+ public ThreadReferenceProxyImpl(VirtualMachineProxyImpl virtualMachineProxy, ThreadReference threadReference) {
+ super(virtualMachineProxy, threadReference);
+ }
+
+ public ThreadReference getThreadReference() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return (ThreadReference)getObjectReference();
+ }
+
+ public VirtualMachineProxyImpl getVirtualMachine() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return (VirtualMachineProxyImpl) myTimer;
+ }
+
+ public String name() {
+ checkValid();
+ if (myName == null) {
+ try {
+ myName = getThreadReference().name();
+ }
+ catch (ObjectCollectedException e) {
+ myName = "";
+ }
+ catch (IllegalThreadStateException e) {
+ myName = "zombie";
+ }
+ }
+ return myName;
+ }
+
+ public int getSuspendCount() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ //LOG.assertTrue((mySuspendCount > 0) == suspends());
+ try {
+ return getThreadReference().suspendCount();
+ }
+ catch (ObjectCollectedException e) {
+ return 0;
+ }
+ }
+
+ public void suspend() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ getThreadReference().suspend();
+ }
+ catch (ObjectCollectedException ignored) {
+ }
+ clearCaches();
+ }
+
+ @NonNls
+ public String toString() {
+ //noinspection HardCodedStringLiteral
+ @NonNls String threadRefString;
+ try {
+ threadRefString = getThreadReference().toString() ;
+ }
+ catch (ObjectCollectedException e) {
+ threadRefString = "[thread collected]";
+ }
+ return "ThreadReferenceProxyImpl: " + threadRefString + " " + super.toString();
+ }
+
+ public void resume() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ //JDI clears all caches on thread resume !!
+ final ThreadReference threadRef = getThreadReference();
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("before resume" + threadRef);
+ }
+ getVirtualMachineProxy().clearCaches();
+ try {
+ threadRef.resume();
+ }
+ catch (ObjectCollectedException ignored) {
+ }
+ }
+
+ protected void clearCaches() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myFrames = null;
+ myFrameCount = -1;
+ super.clearCaches();
+ }
+
+ public int status() {
+ try {
+ return getThreadReference().status();
+ }
+ catch (ObjectCollectedException e) {
+ return ThreadReference.THREAD_STATUS_ZOMBIE;
+ }
+ }
+
+ public ThreadGroupReferenceProxyImpl threadGroupProxy() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ checkValid();
+ if(myThreadGroupProxy == null) {
+ ThreadGroupReference threadGroupRef;
+ try {
+ threadGroupRef = getThreadReference().threadGroup();
+ }
+ catch (ObjectCollectedException e) {
+ threadGroupRef = null;
+ }
+ myThreadGroupProxy = getVirtualMachineProxy().getThreadGroupReferenceProxy(threadGroupRef);
+ }
+ return myThreadGroupProxy;
+ }
+
+ public int frameCount() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ checkValid();
+ if (myFrameCount == -1) {
+ final ThreadReference threadReference = getThreadReference();
+ try {
+ myFrameCount = threadReference.frameCount();
+ }
+ catch(ObjectCollectedException e) {
+ myFrameCount = 0;
+ }
+ catch (IncompatibleThreadStateException e) {
+ if (!threadReference.isSuspended()) {
+ // give up because it seems to be really resumed
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ else {
+ // JDI bug: although isSuspended() == true, frameCount() may throw IncompatibleThreadStateException
+ // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4783403
+ // unfortunately, impossible to get this information at the moment, so assume the frame count is null
+ myFrameCount = 0;
+ }
+ }
+ }
+ return myFrameCount;
+ }
+
+ public List<StackFrameProxyImpl> frames() throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final ThreadReference threadRef = getThreadReference();
+ try {
+ //LOG.assertTrue(threadRef.isSuspended());
+ checkValid();
+
+ if(myFrames == null) {
+ checkFrames(threadRef);
+
+ myFrames = new ArrayList<StackFrameProxyImpl>(frameCount());
+ for (ListIterator<StackFrameProxyImpl> iterator = myFramesFromBottom.listIterator(frameCount()); iterator.hasPrevious();) {
+ StackFrameProxyImpl stackFrameProxy = iterator.previous();
+ myFrames.add(stackFrameProxy);
+ }
+ }
+ }
+ catch (ObjectCollectedException e) {
+ return Collections.emptyList();
+ }
+ return myFrames;
+ }
+
+ private void checkFrames(@NotNull final ThreadReference threadRef) throws EvaluateException {
+ if (myFramesFromBottom.size() < frameCount()) {
+ int count = frameCount();
+ List<StackFrame> frames;
+ try {
+ frames = threadRef.frames(0, count - myFramesFromBottom.size());
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (InternalException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+
+ int index = myFramesFromBottom.size() + 1;
+ for (ListIterator<StackFrame> iterator = frames.listIterator(count - myFramesFromBottom.size()); iterator.hasPrevious();) {
+ StackFrame stackFrame = iterator.previous();
+ myFramesFromBottom.add(new StackFrameProxyImpl(this, stackFrame, index));
+ index++;
+ }
+ }
+ }
+
+ public StackFrameProxyImpl frame(int i) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final ThreadReference threadReference = getThreadReference();
+ try {
+ if(!threadReference.isSuspended()) {
+ return null;
+ }
+ checkFrames(threadReference);
+ final int frameCount = frameCount();
+ if (frameCount == 0) {
+ return null;
+ }
+ return myFramesFromBottom.get(frameCount - i - 1);
+ }
+ catch (ObjectCollectedException e) {
+ return null;
+ }
+ catch (IllegalThreadStateException e) {
+ return null;
+ }
+ }
+
+ public void popFrames(StackFrameProxyImpl stackFrame) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ getThreadReference().popFrames(stackFrame.getStackFrame());
+ }
+ catch (InvalidStackFrameException ignored) {
+ }
+ catch (ObjectCollectedException ignored) {
+ }
+ catch (InternalException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ catch (IncompatibleThreadStateException e) {
+ throw EvaluateExceptionUtil.createEvaluateException(e);
+ }
+ finally {
+ clearCaches();
+ getVirtualMachineProxy().clearCaches();
+ }
+ }
+
+ public boolean isSuspended() throws ObjectCollectedException {
+ try {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return getThreadReference().isSuspended();
+ }
+ catch (IllegalThreadStateException e) {
+ // must be zombie thread
+ LOG.info(e);
+ return false;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java
new file mode 100644
index 0000000..2b4c6b4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java
@@ -0,0 +1,678 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.jdi;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.jdi.VirtualMachineProxy;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.HashMap;
+import com.sun.jdi.*;
+import com.sun.jdi.event.EventQueue;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.tools.jdi.VoidValueImpl;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class VirtualMachineProxyImpl implements JdiTimer, VirtualMachineProxy {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.VirtualMachineProxyImpl");
+ private final DebugProcessImpl myDebugProcess;
+ private final VirtualMachine myVirtualMachine;
+ private int myTimeStamp = 0;
+ private int myPausePressedCount = 0;
+
+ // cached data
+ private final Map<ObjectReference, ObjectReferenceProxyImpl> myObjectReferenceProxies = new HashMap<ObjectReference, ObjectReferenceProxyImpl>();
+ private Map<ThreadReference, ThreadReferenceProxyImpl> myAllThreads = new HashMap<ThreadReference, ThreadReferenceProxyImpl>();
+ private final Map<ThreadGroupReference, ThreadGroupReferenceProxyImpl> myThreadGroups = new HashMap<ThreadGroupReference, ThreadGroupReferenceProxyImpl>();
+ private boolean myAllThreadsDirty = true;
+ private List<ReferenceType> myAllClasses;
+ private Map<ReferenceType, List<ReferenceType>> myNestedClassesCache = new HashMap<ReferenceType, List<ReferenceType>>();
+
+ public Throwable mySuspendLogger = new Throwable();
+ private final boolean myVersionHigher_15;
+ private final boolean myVersionHigher_14;
+
+ public VirtualMachineProxyImpl(DebugProcessImpl debugProcess, @NotNull VirtualMachine virtualMachine) {
+ myVirtualMachine = virtualMachine;
+ myDebugProcess = debugProcess;
+
+ myVersionHigher_15 = versionHigher("1.5");
+ myVersionHigher_14 = myVersionHigher_15 || versionHigher("1.4");
+
+ // avoid lazy-init for some properties: the following will pre-calculate values
+ canRedefineClasses();
+ canWatchFieldModification();
+ canPopFrames();
+
+ List<ThreadGroupReference> groups = virtualMachine.topLevelThreadGroups();
+ for (ThreadGroupReference threadGroupReference : groups) {
+ threadGroupCreated(threadGroupReference);
+ }
+ }
+
+ public VirtualMachine getVirtualMachine() {
+ return myVirtualMachine;
+ }
+
+ public List<ReferenceType> classesByName(String s) {
+ return myVirtualMachine.classesByName(s);
+ }
+
+ public List<ReferenceType> nestedTypes(ReferenceType refType) {
+ List<ReferenceType> nestedTypes = myNestedClassesCache.get(refType);
+ if (nestedTypes == null) {
+ final List<ReferenceType> list = refType.nestedTypes();
+ final int size = list.size();
+ if (size > 0) {
+ final Set<ReferenceType> candidates = new HashSet<ReferenceType>();
+ final ClassLoaderReference outerLoader = refType.classLoader();
+ for (ReferenceType nested : list) {
+ try {
+ if (outerLoader == null? nested.classLoader() == null : outerLoader.equals(nested.classLoader())) {
+ candidates.add(nested);
+ }
+ }
+ catch (ObjectCollectedException ignored) {
+ }
+ }
+
+ if (!candidates.isEmpty()) {
+ // keep only direct nested types
+ final Set<ReferenceType> nested2 = new HashSet<ReferenceType>();
+ for (final ReferenceType candidate : candidates) {
+ nested2.addAll(nestedTypes(candidate));
+ }
+ candidates.removeAll(nested2);
+ }
+
+ nestedTypes = candidates.isEmpty()? Collections.<ReferenceType>emptyList() : new ArrayList<ReferenceType>(candidates);
+ }
+ else {
+ nestedTypes = Collections.emptyList();
+ }
+ myNestedClassesCache.put(refType, nestedTypes);
+ }
+ return nestedTypes;
+ }
+
+ public List<ReferenceType> allClasses() {
+ if (myAllClasses == null) {
+ myAllClasses = myVirtualMachine.allClasses();
+ }
+ return myAllClasses;
+ }
+
+ public String toString() {
+ return myVirtualMachine.toString();
+ }
+
+ public void redefineClasses(Map<ReferenceType, byte[]> map) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ myVirtualMachine.redefineClasses(map);
+ }
+ finally {
+ clearCaches();
+ }
+ }
+
+ /**
+ * @return a list of all ThreadReferenceProxies
+ */
+ public Collection<ThreadReferenceProxyImpl> allThreads() {
+ if(myAllThreadsDirty) {
+ myAllThreadsDirty = false;
+
+ final List<ThreadReference> currentThreads = myVirtualMachine.allThreads();
+ final Map<ThreadReference, ThreadReferenceProxyImpl> result = new HashMap<ThreadReference, ThreadReferenceProxyImpl>();
+
+ for (final ThreadReference threadReference : currentThreads) {
+ ThreadReferenceProxyImpl proxy = myAllThreads.get(threadReference);
+ if(proxy == null) {
+ proxy = new ThreadReferenceProxyImpl(this, threadReference);
+ }
+ result.put(threadReference, proxy);
+ }
+ myAllThreads = result;
+ }
+
+ return myAllThreads.values();
+ }
+
+ public void threadStarted(ThreadReference thread) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final Map<ThreadReference, ThreadReferenceProxyImpl> allThreads = myAllThreads;
+ if (allThreads != null && !allThreads.containsKey(thread)) {
+ allThreads.put(thread, new ThreadReferenceProxyImpl(this, thread));
+ }
+ }
+
+ public void threadStopped(ThreadReference thread) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final Map<ThreadReference, ThreadReferenceProxyImpl> allThreads = myAllThreads;
+ if (allThreads != null) {
+ allThreads.remove(thread);
+ }
+ }
+
+ public void suspend() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myPausePressedCount++;
+ myVirtualMachine.suspend();
+ clearCaches();
+ }
+
+ public void resume() {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (myPausePressedCount > 0) {
+ myPausePressedCount--;
+ }
+ clearCaches();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("before resume VM");
+ }
+ try {
+ myVirtualMachine.resume();
+ }
+ catch (InternalException e) {
+ // ok to ignore. Although documentation says it is safe to invoke resume() on running VM,
+ // sometimes this leads to com.sun.jdi.InternalException: Unexpected JDWP Error: 13 (THREAD_NOT_SUSPENDED)
+ LOG.info(e);
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("VM resumed");
+ }
+ //logThreads();
+ }
+
+ /**
+ * @return a list of threadGroupProxies
+ */
+ public List<ThreadGroupReferenceProxyImpl> topLevelThreadGroups() {
+ List<ThreadGroupReference> list = getVirtualMachine().topLevelThreadGroups();
+
+ List<ThreadGroupReferenceProxyImpl> result = new ArrayList<ThreadGroupReferenceProxyImpl>(list.size());
+
+ for (ThreadGroupReference threadGroup : list) {
+ result.add(getThreadGroupReferenceProxy(threadGroup));
+ }
+
+ return result;
+ }
+
+ public void threadGroupCreated(ThreadGroupReference threadGroupReference){
+ if(!isJ2ME()) {
+ ThreadGroupReferenceProxyImpl proxy = new ThreadGroupReferenceProxyImpl(this, threadGroupReference);
+ myThreadGroups.put(threadGroupReference, proxy);
+ }
+ }
+
+ public boolean isJ2ME() {
+ return isJ2ME(getVirtualMachine());
+ }
+
+ private static boolean isJ2ME(VirtualMachine virtualMachine) {
+ return virtualMachine.version().startsWith("1.0");
+ }
+
+ public void threadGroupRemoved(ThreadGroupReference threadGroupReference){
+ myThreadGroups.remove(threadGroupReference);
+ }
+
+ public EventQueue eventQueue() {
+ return myVirtualMachine.eventQueue();
+ }
+
+ public EventRequestManager eventRequestManager() {
+ return myVirtualMachine.eventRequestManager();
+ }
+
+ public VoidValue mirrorOf() throws EvaluateException {
+ try {
+ Constructor<VoidValueImpl> constructor = VoidValueImpl.class.getDeclaredConstructor(new Class[]{VirtualMachine.class});
+ constructor.setAccessible(true);
+ return constructor.newInstance(myVirtualMachine);
+ }
+ catch (NoSuchMethodException e) {
+ LOG.error(e);
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ LOG.error(e);
+ }
+ catch (InstantiationException e) {
+ LOG.error(e);
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.cannot.create.void.value"));
+ }
+
+ public BooleanValue mirrorOf(boolean b) {
+ return myVirtualMachine.mirrorOf(b);
+ }
+
+ public ByteValue mirrorOf(byte b) {
+ return myVirtualMachine.mirrorOf(b);
+ }
+
+ public CharValue mirrorOf(char c) {
+ return myVirtualMachine.mirrorOf(c);
+ }
+
+ public ShortValue mirrorOf(short i) {
+ return myVirtualMachine.mirrorOf(i);
+ }
+
+ public IntegerValue mirrorOf(int i) {
+ return myVirtualMachine.mirrorOf(i);
+ }
+
+ public LongValue mirrorOf(long l) {
+ return myVirtualMachine.mirrorOf(l);
+ }
+
+ public FloatValue mirrorOf(float v) {
+ return myVirtualMachine.mirrorOf(v);
+ }
+
+ public DoubleValue mirrorOf(double v) {
+ return myVirtualMachine.mirrorOf(v);
+ }
+
+ public StringReference mirrorOf(String s) {
+ return myVirtualMachine.mirrorOf(s);
+ }
+
+ public Process process() {
+ return myVirtualMachine.process();
+ }
+
+ public void dispose() {
+ myVirtualMachine.dispose();
+ }
+
+ public void exit(int i) {
+ myVirtualMachine.exit(i);
+ }
+
+ private final Capability myWatchFielsModification = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canWatchFieldModification();
+ }
+ };
+ public boolean canWatchFieldModification() {
+ return myWatchFielsModification.isAvailable();
+ }
+
+ private final Capability myWatchFieldAccess = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canWatchFieldAccess();
+ }
+ };
+ public boolean canWatchFieldAccess() {
+ return myWatchFieldAccess.isAvailable();
+ }
+
+ private final Capability myInvokeMethods = new Capability() {
+ protected boolean calcValue() {
+ return isJ2ME();
+ }
+ };
+ public boolean canInvokeMethods() {
+ return myInvokeMethods.isAvailable();
+ }
+
+ private final Capability myGetBytecodes = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canGetBytecodes();
+ }
+ };
+ public boolean canGetBytecodes() {
+ return myGetBytecodes.isAvailable();
+ }
+
+ private final Capability myGetSyntheticAttribute = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canGetSyntheticAttribute();
+ }
+ };
+ public boolean canGetSyntheticAttribute() {
+ return myGetSyntheticAttribute.isAvailable();
+ }
+
+ private final Capability myGetOwnedMonitorInfo = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canGetOwnedMonitorInfo();
+ }
+ };
+ public boolean canGetOwnedMonitorInfo() {
+ return myGetOwnedMonitorInfo.isAvailable();
+ }
+
+ private final Capability myGetMonitorFrameInfo = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canGetMonitorFrameInfo();
+ }
+ };
+ public boolean canGetMonitorFrameInfo() {
+ return myGetMonitorFrameInfo.isAvailable();
+ }
+
+ private final Capability myGetCurrentContendedMonitor = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canGetCurrentContendedMonitor();
+ }
+ };
+ public boolean canGetCurrentContendedMonitor() {
+ return myGetCurrentContendedMonitor.isAvailable();
+ }
+
+ private final Capability myGetMonitorInfo = new Capability() {
+ protected boolean calcValue() {
+ return myVirtualMachine.canGetMonitorInfo();
+ }
+ };
+ public boolean canGetMonitorInfo() {
+ return myGetMonitorInfo.isAvailable();
+ }
+
+ private final Capability myUseInstanceFilters = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canUseInstanceFilters();
+ }
+ };
+ public boolean canUseInstanceFilters() {
+ return myUseInstanceFilters.isAvailable();
+ }
+
+ private final Capability myRedefineClasses = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canRedefineClasses();
+ }
+ };
+ public boolean canRedefineClasses() {
+ return myRedefineClasses.isAvailable();
+ }
+
+ private final Capability myAddMethod = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canAddMethod();
+ }
+ };
+ public boolean canAddMethod() {
+ return myAddMethod.isAvailable();
+ }
+
+ private final Capability myUnrestrictedlyRedefineClasses = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canUnrestrictedlyRedefineClasses();
+ }
+ };
+ public boolean canUnrestrictedlyRedefineClasses() {
+ return myUnrestrictedlyRedefineClasses.isAvailable();
+ }
+
+ private final Capability myPopFrames = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canPopFrames();
+ }
+ };
+ public boolean canPopFrames() {
+ return myPopFrames.isAvailable();
+ }
+
+ private final Capability myCanGetInstanceInfo = new Capability() {
+ protected boolean calcValue() {
+ if (!myVersionHigher_15) {
+ return false;
+ }
+ try {
+ final Method method = VirtualMachine.class.getMethod("canGetInstanceInfo");
+ return (Boolean)method.invoke(myVirtualMachine);
+ }
+ catch (NoSuchMethodException ignored) {
+ }
+ catch (IllegalAccessException e) {
+ LOG.error(e);
+ }
+ catch (InvocationTargetException e) {
+ LOG.error(e);
+ }
+ return false;
+ }
+ };
+ public boolean canGetInstanceInfo() {
+ return myCanGetInstanceInfo.isAvailable();
+ }
+
+ public final boolean versionHigher(String version) {
+ return myVirtualMachine.version().compareTo(version) >= 0;
+ }
+
+ private final Capability myGetSourceDebugExtension = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canGetSourceDebugExtension();
+ }
+ };
+ public boolean canGetSourceDebugExtension() {
+ return myGetSourceDebugExtension.isAvailable();
+ }
+
+ private final Capability myRequestVMDeathEvent = new Capability() {
+ protected boolean calcValue() {
+ return myVersionHigher_14 && myVirtualMachine.canRequestVMDeathEvent();
+ }
+ };
+ public boolean canRequestVMDeathEvent() {
+ return myRequestVMDeathEvent.isAvailable();
+ }
+
+ private final Capability myGetMethodReturnValues = new Capability() {
+ protected boolean calcValue() {
+ if (myVersionHigher_15) {
+ //return myVirtualMachine.canGetMethodReturnValues();
+ try {
+ //noinspection HardCodedStringLiteral
+ final Method method = VirtualMachine.class.getDeclaredMethod("canGetMethodReturnValues");
+ final Boolean rv = (Boolean)method.invoke(myVirtualMachine);
+ return rv.booleanValue();
+ }
+ catch (NoSuchMethodException ignored) {
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ catch (InvocationTargetException ignored) {
+ }
+ }
+ return false;
+ }
+ };
+ public boolean canGetMethodReturnValues() {
+ return myGetMethodReturnValues.isAvailable();
+ }
+
+ public String getDefaultStratum() {
+ return myVersionHigher_14 ? myVirtualMachine.getDefaultStratum() : null;
+ }
+
+ public String description() {
+ return myVirtualMachine.description();
+ }
+
+ public String version() {
+ return myVirtualMachine.version();
+ }
+
+ public String name() {
+ return myVirtualMachine.name();
+ }
+
+ public void setDebugTraceMode(int i) {
+ myVirtualMachine.setDebugTraceMode(i);
+ }
+
+ public ThreadReferenceProxyImpl getThreadReferenceProxy(ThreadReference thread) {
+ if(thread == null) {
+ return null;
+ }
+
+ ThreadReferenceProxyImpl proxy = myAllThreads.get(thread);
+ if(proxy == null) {
+ proxy = new ThreadReferenceProxyImpl(this, thread);
+ myAllThreads.put(thread, proxy);
+ }
+
+ return proxy;
+ }
+
+ public ThreadGroupReferenceProxyImpl getThreadGroupReferenceProxy(ThreadGroupReference group) {
+ if(group == null) {
+ return null;
+ }
+
+ ThreadGroupReferenceProxyImpl proxy = myThreadGroups.get(group);
+ if(proxy == null) {
+ if(!isJ2ME()) {
+ proxy = new ThreadGroupReferenceProxyImpl(this, group);
+ myThreadGroups.put(group, proxy);
+ }
+ }
+
+ return proxy;
+ }
+
+ public ObjectReferenceProxyImpl getObjectReferenceProxy(ObjectReference objectReference) {
+ if (objectReference != null) {
+ if (objectReference instanceof ThreadReference) {
+ return getThreadReferenceProxy((ThreadReference)objectReference);
+ }
+ else if (objectReference instanceof ThreadGroupReference) {
+ return getThreadGroupReferenceProxy((ThreadGroupReference)objectReference);
+ }
+ else {
+ ObjectReferenceProxyImpl proxy = myObjectReferenceProxies.get(objectReference);
+ if (proxy == null) {
+ if (objectReference instanceof StringReference) {
+ proxy = new StringReferenceProxy(this, (StringReference)objectReference);
+ }
+ else {
+ proxy = new ObjectReferenceProxyImpl(this, objectReference);
+ }
+ myObjectReferenceProxies.put(objectReference, proxy);
+ }
+ return proxy;
+ }
+ }
+ return null;
+ }
+
+ public boolean equals(Object obj) {
+ LOG.assertTrue(obj instanceof VirtualMachineProxyImpl);
+ return myVirtualMachine.equals(((VirtualMachineProxyImpl)obj).getVirtualMachine());
+ }
+
+ public int hashCode() {
+ return myVirtualMachine.hashCode();
+ }
+
+ public void clearCaches() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("VM cleared");
+ }
+
+ myAllClasses = null;
+ if (!myNestedClassesCache.isEmpty()) {
+ myNestedClassesCache = new HashMap<ReferenceType, List<ReferenceType>>(myNestedClassesCache.size());
+ }
+ //myAllThreadsDirty = true;
+ myTimeStamp++;
+ }
+
+ public int getCurrentTime() {
+ return myTimeStamp;
+ }
+
+ public DebugProcess getDebugProcess() {
+ return myDebugProcess;
+ }
+
+ public static boolean isCollected(ObjectReference reference) {
+ return !isJ2ME(reference.virtualMachine()) && reference.isCollected();
+ }
+
+ public String getResumeStack() {
+ return StringUtil.getThrowableText(mySuspendLogger);
+ }
+
+ public boolean isPausePressed() {
+ return myPausePressedCount > 0;
+ }
+
+ public boolean isSuspended() {
+ for (ThreadReferenceProxyImpl thread : allThreads()) {
+ if (thread.getSuspendCount() != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void logThreads() {
+ if(LOG.isDebugEnabled()) {
+ for (ThreadReferenceProxyImpl thread : allThreads()) {
+ if (!thread.isCollected()) {
+ LOG.debug("suspends " + thread + " " + thread.getSuspendCount() + " " + thread.isSuspended());
+ }
+ }
+ }
+ }
+
+
+ private abstract static class Capability {
+ private Boolean myValue = null;
+
+ public final boolean isAvailable() {
+ if (myValue == null) {
+ try {
+ myValue = Boolean.valueOf(calcValue());
+ }
+ catch (VMDisconnectedException e) {
+ LOG.info(e);
+ myValue = Boolean.FALSE;
+ }
+ }
+ return myValue.booleanValue();
+ }
+
+ protected abstract boolean calcValue();
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ArrayRendererConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/ArrayRendererConfigurable.java
new file mode 100644
index 0000000..eeb0986
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/ArrayRendererConfigurable.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.ui.tree.render.ArrayRenderer;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.awt.*;
+
+public class ArrayRendererConfigurable implements UnnamedConfigurable{
+ private JTextField myEntriesLimit;
+ private JTextField myStartIndex;
+ private JTextField myEndIndex;
+ private boolean myEntriesLimitUpdateEnabled = true;
+ private boolean myIndexUpdateEnabled = true;
+
+ private final ArrayRenderer myRenderer;
+ private JComponent myPanel;
+
+ public ArrayRendererConfigurable(ArrayRenderer renderer) {
+ myRenderer = renderer;
+ }
+
+ public ArrayRenderer getRenderer() {
+ return myRenderer;
+ }
+
+ public void reset() {
+ myStartIndex.setText(String.valueOf(myRenderer.START_INDEX));
+ myEndIndex.setText(String.valueOf(myRenderer.END_INDEX));
+ myEntriesLimit.setText(String.valueOf(myRenderer.ENTRIES_LIMIT));
+ }
+
+ public void apply() {
+ applyTo(myRenderer, true);
+ }
+
+ private void applyTo(ArrayRenderer renderer, boolean showBigRangeWarning) {
+ int newStartIndex = getInt(myStartIndex);
+ int newEndIndex = getInt(myEndIndex);
+ int newLimit = getInt(myEntriesLimit);
+
+ if (newStartIndex >= 0 && newEndIndex >= 0) {
+ if (newStartIndex >= newEndIndex) {
+ int currentStartIndex = renderer.START_INDEX;
+ int currentEndIndex = renderer.END_INDEX;
+ newEndIndex = newStartIndex + (currentEndIndex - currentStartIndex);
+ }
+
+ if(newLimit <= 0) {
+ newLimit = 1;
+ }
+
+ if(showBigRangeWarning && (newEndIndex - newStartIndex > 10000)) {
+ final int answer = Messages.showOkCancelDialog(
+ myPanel.getRootPane(),
+ DebuggerBundle.message("warning.range.too.big", ApplicationNamesInfo.getInstance().getProductName()),
+ DebuggerBundle.message("title.range.too.big"),
+ Messages.getWarningIcon());
+ if(answer != DialogWrapper.OK_EXIT_CODE) {
+ return;
+ }
+ }
+ }
+
+ renderer.START_INDEX = newStartIndex;
+ renderer.END_INDEX = newEndIndex;
+ renderer.ENTRIES_LIMIT = newLimit;
+ }
+
+ public JComponent createComponent() {
+ myPanel = new JPanel(new GridBagLayout());
+
+ myStartIndex = new JTextField(5);
+ myEndIndex = new JTextField(5);
+ myEntriesLimit = new JTextField(5);
+
+ final FontMetrics fontMetrics = myStartIndex.getFontMetrics(myStartIndex.getFont());
+ final Dimension minSize = new Dimension(myStartIndex.getPreferredSize());
+ //noinspection HardCodedStringLiteral
+ minSize.width = fontMetrics.stringWidth("AAAAA");
+ myStartIndex.setMinimumSize(minSize);
+ myEndIndex.setMinimumSize(minSize);
+ myEntriesLimit.setMinimumSize(minSize);
+
+ JLabel startIndexLabel = new JLabel(DebuggerBundle.message("label.array.renderer.configurable.start.index"));
+ startIndexLabel.setLabelFor(myStartIndex);
+
+ JLabel endIndexLabel = new JLabel(DebuggerBundle.message("label.array.renderer.configurable.end.index"));
+ endIndexLabel.setLabelFor(myEndIndex);
+
+ JLabel entriesLimitLabel = new JLabel(DebuggerBundle.message("label.array.renderer.configurable.max.count1"));
+ entriesLimitLabel.setLabelFor(myEntriesLimit);
+
+ myPanel.add(startIndexLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 8), 0, 0));
+ myPanel.add(myStartIndex, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 8), 0, 0));
+ myPanel.add(endIndexLabel, new GridBagConstraints(2, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 8), 0, 0));
+ myPanel.add(myEndIndex, new GridBagConstraints(3, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+
+ myPanel.add(entriesLimitLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 8), 0, 0));
+ myPanel.add(myEntriesLimit, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 8), 0, 0));
+ myPanel.add(new JLabel(DebuggerBundle.message("label.array.renderer.configurable.max.count2")), new GridBagConstraints(2, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+
+ final DocumentListener listener = new DocumentListener() {
+ private void updateEntriesLimit() {
+ final boolean state = myIndexUpdateEnabled;
+ myIndexUpdateEnabled = false;
+ try {
+ if (myEntriesLimitUpdateEnabled) {
+ myEntriesLimit.setText(String.valueOf(getInt(myEndIndex) - getInt(myStartIndex) + 1));
+ }
+ }
+ finally {
+ myIndexUpdateEnabled = state;
+ }
+ }
+ public void changedUpdate(DocumentEvent e) {
+ updateEntriesLimit();
+ }
+ public void insertUpdate (DocumentEvent e) {
+ updateEntriesLimit();
+ }
+ public void removeUpdate (DocumentEvent e) {
+ updateEntriesLimit();
+ }
+ };
+ myStartIndex.getDocument().addDocumentListener(listener);
+ myEndIndex.getDocument().addDocumentListener(listener);
+ myEntriesLimit.getDocument().addDocumentListener(new DocumentListener() {
+ private void updateEndIndex() {
+ final boolean state = myEntriesLimitUpdateEnabled;
+ myEntriesLimitUpdateEnabled = false;
+ try {
+ if (myIndexUpdateEnabled) {
+ myEndIndex.setText(String.valueOf(getInt(myEntriesLimit) + getInt(myStartIndex) - 1));
+ }
+ }
+ finally {
+ myEntriesLimitUpdateEnabled = state;
+ }
+ }
+ public void insertUpdate(DocumentEvent e) {
+ updateEndIndex();
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ updateEndIndex();
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ updateEndIndex();
+ }
+ });
+ return myPanel;
+ }
+
+ private static int getInt(JTextField textField) {
+ int newEndIndex = 0;
+ try {
+ newEndIndex = Integer.parseInt(textField.getText().trim());
+ }
+ catch (NumberFormatException exception) {
+ // ignored
+ }
+ return newEndIndex;
+ }
+
+ public boolean isModified() {
+ ArrayRenderer cloneRenderer = myRenderer.clone();
+ applyTo(cloneRenderer, false);
+ final boolean valuesEqual =
+ (myRenderer.END_INDEX == cloneRenderer.END_INDEX) &&
+ (myRenderer.START_INDEX == cloneRenderer.START_INDEX) &&
+ (myRenderer.ENTRIES_LIMIT == cloneRenderer.ENTRIES_LIMIT);
+ return !valuesEqual;
+ }
+
+ public void disposeUIResources() {
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CompositeDataBinding.java b/java/debugger/impl/src/com/intellij/debugger/settings/CompositeDataBinding.java
new file mode 100644
index 0000000..896fe54
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/CompositeDataBinding.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 12, 2005
+ */
+public class CompositeDataBinding implements DataBinding{
+ private final List<DataBinding> myBindings = new ArrayList<DataBinding>();
+
+ void addBinding(DataBinding binding) {
+ myBindings.add(binding);
+ }
+
+ public void loadData(Object from) {
+ for (Iterator<DataBinding> it = myBindings.iterator(); it.hasNext();) {
+ it.next().loadData(from);
+ }
+ }
+
+ public void saveData(Object to) {
+ for (Iterator<DataBinding> it = myBindings.iterator(); it.hasNext();) {
+ it.next().saveData(to);
+ }
+ }
+
+ public boolean isModified(Object obj) {
+ for (Iterator<DataBinding> it = myBindings.iterator(); it.hasNext();) {
+ if (it.next().isModified(obj)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java
new file mode 100644
index 0000000..2f92bc5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java
@@ -0,0 +1,497 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.DebuggerExpressionTextField;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.debugger.ui.tree.render.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.AnActionButton;
+import com.intellij.ui.AnActionButtonRunnable;
+import com.intellij.ui.TableUtil;
+import com.intellij.ui.ToolbarDecorator;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ui.AbstractTableCellEditor;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 24, 2005
+ */
+public class CompoundRendererConfigurable implements UnnamedConfigurable {
+ private CompoundReferenceRenderer myRenderer;
+ private CompoundReferenceRenderer myOriginalRenderer;
+ private Project myProject;
+ private TextFieldWithBrowseButton myClassNameField;
+ private JRadioButton myRbDefaultLabel;
+ private JRadioButton myRbExpressionLabel;
+ private JRadioButton myRbDefaultChildrenRenderer;
+ private JRadioButton myRbExpressionChildrenRenderer;
+ private JRadioButton myRbListChildrenRenderer;
+ private DebuggerExpressionTextField myLabelEditor;
+ private DebuggerExpressionTextField myChildrenEditor;
+ private DebuggerExpressionTextField myChildrenExpandedEditor;
+ private DebuggerExpressionTextField myListChildrenEditor;
+ private JComponent myChildrenListEditor;
+ private JLabel myExpandedLabel;
+ private JPanel myMainPanel;
+ private JBTable myTable;
+ @NonNls private static final String EMPTY_PANEL_ID = "EMPTY";
+ @NonNls private static final String DATA_PANEL_ID = "DATA";
+ private static final int NAME_TABLE_COLUMN = 0;
+ private static final int EXPRESSION_TABLE_COLUMN = 1;
+
+ public CompoundRendererConfigurable(@Nullable Project project) {
+ myProject = project;
+ }
+
+ public void setRenderer(NodeRenderer renderer) {
+ if (renderer instanceof CompoundReferenceRenderer) {
+ myRenderer = (CompoundReferenceRenderer)renderer;
+ myOriginalRenderer = (CompoundReferenceRenderer)renderer.clone();
+ }
+ else {
+ myRenderer = myOriginalRenderer = null;
+ }
+ reset();
+ }
+
+ public CompoundReferenceRenderer getRenderer() {
+ return myRenderer;
+ }
+
+ public JComponent createComponent() {
+ if (myProject == null) {
+ myProject = JavaDebuggerSupport.getCurrentProject();
+ }
+ final JPanel panel = new JPanel(new GridBagLayout());
+
+ myRbDefaultLabel = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.default.renderer"));
+ myRbExpressionLabel = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.expression"));
+ final ButtonGroup labelButtonsGroup = new ButtonGroup();
+ labelButtonsGroup.add(myRbDefaultLabel);
+ labelButtonsGroup.add(myRbExpressionLabel);
+
+ myRbDefaultChildrenRenderer = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.default.renderer"));
+ myRbExpressionChildrenRenderer = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.expression"));
+ myRbListChildrenRenderer = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.expression.list"));
+ final ButtonGroup childrenButtonGroup = new ButtonGroup();
+ childrenButtonGroup.add(myRbDefaultChildrenRenderer);
+ childrenButtonGroup.add(myRbExpressionChildrenRenderer);
+ childrenButtonGroup.add(myRbListChildrenRenderer);
+
+ myLabelEditor = new DebuggerExpressionTextField(myProject, null, "ClassLabelExpression");
+ myChildrenEditor = new DebuggerExpressionTextField(myProject, null, "ClassChildrenExpression");
+ myChildrenExpandedEditor = new DebuggerExpressionTextField(myProject, null, "ClassChildrenExpression");
+ myChildrenListEditor = createChildrenListEditor();
+
+ final ItemListener updateListener = new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ updateEnabledState();
+ }
+ };
+ myRbExpressionLabel.addItemListener(updateListener);
+ myRbListChildrenRenderer.addItemListener(updateListener);
+ myRbExpressionChildrenRenderer.addItemListener(updateListener);
+
+ myClassNameField = new TextFieldWithBrowseButton(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ PsiClass psiClass = DebuggerUtils.getInstance()
+ .chooseClassDialog(DebuggerBundle.message("title.compound.renderer.configurable.choose.renderer.reference.type"), myProject);
+ if (psiClass != null) {
+ myClassNameField.setText(JVMNameUtil.getNonAnonymousClassName(psiClass));
+ }
+ }
+ });
+ myClassNameField.getTextField().addFocusListener(new FocusAdapter() {
+ public void focusLost(FocusEvent e) {
+ final String qName = myClassNameField.getText();
+ updateContext(qName);
+ }
+ });
+
+ panel.add(new JLabel(DebuggerBundle.message("label.compound.renderer.configurable.apply.to")),
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ panel.add(myClassNameField, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0));
+
+ panel.add(new JLabel(DebuggerBundle.message("label.compound.renderer.configurable.when.rendering")),
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(20, 0, 0, 0), 0, 0));
+ panel.add(myRbDefaultLabel,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 10, 0, 0), 0, 0));
+ panel.add(myRbExpressionLabel,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 10, 0, 0), 0, 0));
+ panel.add(myLabelEditor, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 30, 0, 0), 0, 0));
+
+ panel.add(new JLabel(DebuggerBundle.message("label.compound.renderer.configurable.when.expanding")),
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(20, 0, 0, 0), 0, 0));
+ panel.add(myRbDefaultChildrenRenderer,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 10, 0, 0), 0, 0));
+ panel.add(myRbExpressionChildrenRenderer,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 10, 0, 0), 0, 0));
+ panel.add(myChildrenEditor, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 30, 0, 0), 0, 0));
+ myExpandedLabel = new JLabel(DebuggerBundle.message("label.compound.renderer.configurable.test.can.expand"));
+ panel.add(myExpandedLabel,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(4, 30, 0, 0), 0, 0));
+ panel.add(myChildrenExpandedEditor, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 30, 0, 0), 0, 0));
+ panel.add(myRbListChildrenRenderer, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 10, 0, 0), 0, 0));
+ panel.add(myChildrenListEditor,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
+ new Insets(4, 30, 0, 0), 0, 0));
+
+ myMainPanel = new JPanel(new CardLayout());
+ myMainPanel.add(new JPanel(), EMPTY_PANEL_ID);
+ myMainPanel.add(panel, DATA_PANEL_ID);
+ return myMainPanel;
+ }
+
+ private void updateContext(final String qName) {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final Project project = myProject;
+ final PsiClass psiClass = project != null ? DebuggerUtils.findClass(qName, project, GlobalSearchScope.allScope(project)) : null;
+ myLabelEditor.setContext(psiClass);
+ myChildrenEditor.setContext(psiClass);
+ myChildrenExpandedEditor.setContext(psiClass);
+ myListChildrenEditor.setContext(psiClass);
+ }
+ });
+ }
+
+ private void updateEnabledState() {
+ myLabelEditor.setEnabled(myRbExpressionLabel.isSelected());
+
+ final boolean isChildrenExpression = myRbExpressionChildrenRenderer.isSelected();
+ myChildrenExpandedEditor.setEnabled(isChildrenExpression);
+ myExpandedLabel.setEnabled(isChildrenExpression);
+ myChildrenEditor.setEnabled(isChildrenExpression);
+ myTable.setEnabled(myRbListChildrenRenderer.isSelected());
+ }
+
+ private JComponent createChildrenListEditor() {
+ final MyTableModel tableModel = new MyTableModel();
+ myTable = new JBTable(tableModel);
+ myListChildrenEditor = new DebuggerExpressionTextField(myProject, null, "NamedChildrenConfigurable");
+
+ final TableColumn exprColumn = myTable.getColumnModel().getColumn(EXPRESSION_TABLE_COLUMN);
+ exprColumn.setCellEditor(new AbstractTableCellEditor() {
+ public Object getCellEditorValue() {
+ return myListChildrenEditor.getText();
+ }
+
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+ myListChildrenEditor.setText((TextWithImports)value);
+ return myListChildrenEditor;
+ }
+ });
+ exprColumn.setCellRenderer(new DefaultTableCellRenderer() {
+ public Component getTableCellRendererComponent(JTable table,
+ Object value,
+ boolean isSelected,
+ boolean hasFocus,
+ int row,
+ int column) {
+ final TextWithImports textWithImports = (TextWithImports)value;
+ final String text = (textWithImports != null) ? textWithImports.getText() : "";
+ return super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column);
+ }
+ });
+
+ return ToolbarDecorator.createDecorator(myTable)
+ .setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ tableModel.addRow("", DebuggerUtils.getInstance().createExpressionWithImports(""));
+ }
+ }).setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ int selectedRow = myTable.getSelectedRow();
+ if (selectedRow >= 0 && selectedRow < myTable.getRowCount()) {
+ getTableModel().removeRow(selectedRow);
+ }
+ }
+ }).setMoveUpAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ TableUtil.moveSelectedItemsUp(myTable);
+ }
+ }).setMoveDownAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ TableUtil.moveSelectedItemsDown(myTable);
+ }
+ }).createPanel();
+ }
+
+ public boolean isModified() {
+ if (myRenderer == null) {
+ return false;
+ }
+ final CompoundReferenceRenderer cloned = (CompoundReferenceRenderer)myRenderer.clone();
+ flushDataTo(cloned);
+ return !DebuggerUtilsEx.externalizableEqual(cloned, myOriginalRenderer);
+ }
+
+ public void apply() throws ConfigurationException {
+ if (myRenderer == null) {
+ return;
+ }
+ flushDataTo(myRenderer);
+ // update the renderer to compare with in order to find out whether we've been modified since last apply
+ myOriginalRenderer = (CompoundReferenceRenderer)myRenderer.clone();
+ }
+
+ private void flushDataTo(final CompoundReferenceRenderer renderer) { // label
+ LabelRenderer labelRenderer = null;
+ if (myRbExpressionLabel.isSelected()) {
+ labelRenderer = new LabelRenderer();
+ labelRenderer.setLabelExpression(myLabelEditor.getText());
+ }
+ renderer.setLabelRenderer(labelRenderer);
+ // children
+ ChildrenRenderer childrenRenderer = null;
+ if (myRbExpressionChildrenRenderer.isSelected()) {
+ childrenRenderer = new ExpressionChildrenRenderer();
+ ((ExpressionChildrenRenderer)childrenRenderer).setChildrenExpression(myChildrenEditor.getText());
+ ((ExpressionChildrenRenderer)childrenRenderer).setChildrenExpandable(myChildrenExpandedEditor.getText());
+ }
+ else if (myRbListChildrenRenderer.isSelected()) {
+ childrenRenderer = new EnumerationChildrenRenderer(getTableModel().getExpressions());
+ }
+ renderer.setChildrenRenderer(childrenRenderer);
+ // classname
+ renderer.setClassName(myClassNameField.getText());
+ }
+
+ public void reset() {
+ final TextWithImports emptyExpressionFragment = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "");
+ ((CardLayout)myMainPanel.getLayout()).show(myMainPanel, myRenderer == null ? EMPTY_PANEL_ID : DATA_PANEL_ID);
+ if (myRenderer == null) {
+ return;
+ }
+ final String className = myRenderer.getClassName();
+ myClassNameField.setText(className);
+ final ValueLabelRenderer labelRenderer = myRenderer.getLabelRenderer();
+ final ChildrenRenderer childrenRenderer = myRenderer.getChildrenRenderer();
+ final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
+
+ if (rendererSettings.isBase(labelRenderer)) {
+ myRbDefaultLabel.setSelected(true);
+ myLabelEditor.setText(emptyExpressionFragment);
+ }
+ else {
+ myRbExpressionLabel.setSelected(true);
+ myLabelEditor.setText(((LabelRenderer)labelRenderer).getLabelExpression());
+ }
+
+ if (rendererSettings.isBase(childrenRenderer)) {
+ myRbDefaultChildrenRenderer.setSelected(true);
+ myChildrenEditor.setText(emptyExpressionFragment);
+ myChildrenExpandedEditor.setText(emptyExpressionFragment);
+ getTableModel().clear();
+ }
+ else if (childrenRenderer instanceof ExpressionChildrenRenderer) {
+ myRbExpressionChildrenRenderer.setSelected(true);
+ final ExpressionChildrenRenderer exprRenderer = (ExpressionChildrenRenderer)childrenRenderer;
+ myChildrenEditor.setText(exprRenderer.getChildrenExpression());
+ myChildrenExpandedEditor.setText(exprRenderer.getChildrenExpandable());
+ getTableModel().clear();
+ }
+ else {
+ myRbListChildrenRenderer.setSelected(true);
+ myChildrenEditor.setText(emptyExpressionFragment);
+ myChildrenExpandedEditor.setText(emptyExpressionFragment);
+ if (childrenRenderer instanceof EnumerationChildrenRenderer) {
+ getTableModel().init(((EnumerationChildrenRenderer)childrenRenderer).getChildren());
+ }
+ else {
+ getTableModel().clear();
+ }
+ }
+
+ updateEnabledState();
+ updateContext(className);
+ }
+
+ public void disposeUIResources() {
+ myRenderer = null;
+ myOriginalRenderer = null;
+ myLabelEditor.dispose();
+ myChildrenEditor.dispose();
+ myChildrenExpandedEditor.dispose();
+ myListChildrenEditor.dispose();
+ myLabelEditor = null;
+ myChildrenEditor = null;
+ myChildrenExpandedEditor = null;
+ myListChildrenEditor = null;
+ myProject = null;
+ }
+
+ private MyTableModel getTableModel() {
+ return (MyTableModel)myTable.getModel();
+ }
+
+ private final class MyTableModel extends AbstractTableModel {
+ private final List<Row> myData = new ArrayList<Row>();
+
+ public MyTableModel() {
+ }
+
+ public void init(List<Pair<String, TextWithImports>> data) {
+ myData.clear();
+ for (final Pair<String, TextWithImports> pair : data) {
+ myData.add(new Row(pair.getFirst(), pair.getSecond()));
+ }
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public int getRowCount() {
+ return myData.size();
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return true;
+ }
+
+ public Class getColumnClass(int columnIndex) {
+ switch (columnIndex) {
+ case NAME_TABLE_COLUMN:
+ return String.class;
+ case EXPRESSION_TABLE_COLUMN:
+ return TextWithImports.class;
+ default:
+ return super.getColumnClass(columnIndex);
+ }
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ if (rowIndex >= getRowCount()) {
+ return null;
+ }
+ final Row row = myData.get(rowIndex);
+ switch (columnIndex) {
+ case NAME_TABLE_COLUMN:
+ return row.name;
+ case EXPRESSION_TABLE_COLUMN:
+ return row.value;
+ default:
+ return null;
+ }
+ }
+
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ if (rowIndex >= getRowCount()) {
+ return;
+ }
+ final Row row = myData.get(rowIndex);
+ switch (columnIndex) {
+ case NAME_TABLE_COLUMN:
+ row.name = (String)aValue;
+ break;
+ case EXPRESSION_TABLE_COLUMN:
+ row.value = (TextWithImports)aValue;
+ break;
+ }
+ }
+
+ public String getColumnName(int columnIndex) {
+ switch (columnIndex) {
+ case NAME_TABLE_COLUMN:
+ return DebuggerBundle.message("label.compound.renderer.configurable.table.header.name");
+ case EXPRESSION_TABLE_COLUMN:
+ return DebuggerBundle.message("label.compound.renderer.configurable.table.header.expression");
+ default:
+ return "";
+ }
+ }
+
+ public void addRow(final String name, final TextWithImports expressionWithImports) {
+ myData.add(new Row(name, expressionWithImports));
+ final int lastRow = myData.size() - 1;
+ fireTableRowsInserted(lastRow, lastRow);
+ }
+
+ public void removeRow(final int row) {
+ if (row >= 0 && row < myData.size()) {
+ myData.remove(row);
+ fireTableRowsDeleted(row, row);
+ }
+ }
+
+ public void clear() {
+ myData.clear();
+ fireTableDataChanged();
+ }
+
+ public List<Pair<String, TextWithImports>> getExpressions() {
+ final ArrayList<Pair<String, TextWithImports>> pairs = new ArrayList<Pair<String, TextWithImports>>(myData.size());
+ for (final Row row : myData) {
+ pairs.add(new Pair<String, TextWithImports>(row.name, row.value));
+ }
+ return pairs;
+ }
+
+ private final class Row {
+ public String name;
+ public TextWithImports value;
+
+ public Row(final String name, final TextWithImports value) {
+ this.name = name;
+ this.value = value;
+ }
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DataBinding.java b/java/debugger/impl/src/com/intellij/debugger/settings/DataBinding.java
new file mode 100644
index 0000000..56de43c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/DataBinding.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 12, 2005
+ */
+public interface DataBinding {
+ void loadData(Object from);
+ void saveData(Object to);
+ boolean isModified(Object obj);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java
new file mode 100644
index 0000000..c1207a5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.debugger.ui.tree.render.ToStringRenderer;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.registry.ui.RegistryCheckBox;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.StateRestoringCheckBox;
+import com.intellij.ui.classFilter.ClassFilterEditor;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+/**
+ * @author Eugene Belyaev
+ */
+public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
+ private JCheckBox myCbAutoscroll;
+ private JCheckBox myCbShowSyntheticFields;
+ private StateRestoringCheckBox myCbShowValFieldsAsLocalVariables;
+ private JCheckBox myCbSort;
+ private JCheckBox myCbHideNullArrayElements;
+ private JCheckBox myCbShowStatic;
+ private JCheckBox myCbShowDeclaredType;
+ private JCheckBox myCbShowFQNames;
+ private JCheckBox myCbShowObjectId;
+
+ private StateRestoringCheckBox myCbShowStaticFinalFields;
+ private final ArrayRendererConfigurable myArrayRendererConfigurable;
+ private JCheckBox myCbEnableAutoExpressions;
+ private JCheckBox myCbEnableAlternateViews;
+
+ private JCheckBox myCbEnableToString;
+ private JRadioButton myRbAllThatOverride;
+ private JRadioButton myRbFromList;
+ private ClassFilterEditor myToStringFilterEditor;
+ private JTextField myValueTooltipDelayField;
+
+ private Project myProject;
+ private RegistryCheckBox myAutoTooltip;
+
+ public DebuggerDataViewsConfigurable(@Nullable Project project) {
+ myProject = project;
+ myArrayRendererConfigurable = new ArrayRendererConfigurable(NodeRendererSettings.getInstance().getArrayRenderer());
+ }
+
+ public void disposeUIResources() {
+ myArrayRendererConfigurable.disposeUIResources();
+ myToStringFilterEditor = null;
+ myProject = null;
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("base.renderer.configurable.display.name");
+ }
+
+ public JComponent createComponent() {
+ if (myProject == null) {
+ myProject = JavaDebuggerSupport.getCurrentProject();
+ }
+ final JPanel panel = new JPanel(new GridBagLayout());
+
+ myCbAutoscroll = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.autoscroll"));
+ myCbShowSyntheticFields = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.synthetic.fields"));
+ myCbShowValFieldsAsLocalVariables = new StateRestoringCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.val.fields.as.locals"));
+ myCbSort = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.sort.alphabetically"));
+ myCbHideNullArrayElements = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.hide.null.array.elements"));
+ myCbShowStatic = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.static.fields"));
+ myCbShowStaticFinalFields = new StateRestoringCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.static.final.fields"));
+ myCbEnableAlternateViews = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.alternate.view"));
+ myCbEnableAutoExpressions = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.auto.expressions"));
+ myCbShowStatic.addChangeListener(new ChangeListener(){
+ public void stateChanged(ChangeEvent e) {
+ if(myCbShowStatic.isSelected()) {
+ myCbShowStaticFinalFields.makeSelectable();
+ }
+ else {
+ myCbShowStaticFinalFields.makeUnselectable(false);
+ }
+ }
+ });
+ myCbShowSyntheticFields.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if(myCbShowSyntheticFields.isSelected()) {
+ myCbShowValFieldsAsLocalVariables.makeSelectable();
+ }
+ else {
+ myCbShowValFieldsAsLocalVariables.makeUnselectable(false);
+ }
+ }
+ });
+ myCbShowDeclaredType = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.declared.type"));
+ myCbShowFQNames = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.fq.names"));
+ myCbShowObjectId = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.show.object.id"));
+
+ myCbEnableToString = new JCheckBox(DebuggerBundle.message("label.base.renderer.configurable.enable.tostring"));
+ myRbAllThatOverride = new JRadioButton(DebuggerBundle.message("label.base.renderer.configurable.all.overridding"));
+ myRbFromList = new JRadioButton(DebuggerBundle.message("label.base.renderer.configurable.classes.from.list"));
+ ButtonGroup group = new ButtonGroup();
+ group.add(myRbAllThatOverride);
+ group.add(myRbFromList);
+ myToStringFilterEditor = new ClassFilterEditor(myProject, null, "reference.viewBreakpoints.classFilters.newPattern");
+ myCbEnableToString.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ final boolean enabled = myCbEnableToString.isSelected();
+ myRbAllThatOverride.setEnabled(enabled);
+ myRbFromList.setEnabled(enabled);
+ myToStringFilterEditor.setEnabled(enabled && myRbFromList.isSelected());
+ }
+ });
+ myRbFromList.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ myToStringFilterEditor.setEnabled(myCbEnableToString.isSelected() && myRbFromList.isSelected());
+ }
+ });
+
+ panel.add(myCbSort, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ panel.add(myCbAutoscroll, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+
+
+ myAutoTooltip = new RegistryCheckBox(Registry.get("debugger.valueTooltipAutoShow"),
+ DebuggerBundle.message("label.base.renderer.configurable.autoTooltip"),
+ DebuggerBundle.message("label.base.renderer.configurable.autoTooltip.description",
+ Registry.stringValue("ide.forcedShowTooltip")));
+ panel.add(myAutoTooltip, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+
+ final JLabel tooltipLabel = new JLabel(DebuggerBundle.message("label.debugger.general.configurable.tooltips.delay"));
+ panel.add(tooltipLabel, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+ myValueTooltipDelayField = new JTextField(10);
+ myValueTooltipDelayField.setMinimumSize(new Dimension(50, myValueTooltipDelayField.getPreferredSize().height));
+ panel.add(myValueTooltipDelayField, new GridBagConstraints(2, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+ tooltipLabel.setLabelFor(myValueTooltipDelayField);
+
+ final JPanel showPanel = new JPanel(new GridBagLayout());
+ showPanel.setBorder(IdeBorderFactory.createTitledBorder("Show", true));
+
+ showPanel.add(myCbShowDeclaredType, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ showPanel.add(myCbShowObjectId, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+ showPanel.add(myCbShowSyntheticFields, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
+ showPanel.add(myCbShowStatic, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 10, 0, 0), 0, 0));
+ showPanel.add(myCbShowValFieldsAsLocalVariables, new GridBagConstraints(2, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 10, 0, 0), 0, 0));
+ showPanel.add(myCbShowStaticFinalFields, new GridBagConstraints(2, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 10, 0, 0), 0, 0));
+ showPanel.add(myCbShowFQNames, new GridBagConstraints(3, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
+
+ panel.add(showPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0));
+
+ final JPanel arraysPanel = new JPanel(new BorderLayout(0, UIUtil.DEFAULT_VGAP));
+ final JComponent arraysComponent = myArrayRendererConfigurable.createComponent();
+ arraysPanel.add(arraysComponent, BorderLayout.CENTER);
+ arraysPanel.add(myCbHideNullArrayElements, BorderLayout.SOUTH);
+ arraysPanel.setBorder(IdeBorderFactory.createTitledBorder("Arrays", true));
+ panel.add(arraysPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+
+ panel.add(myCbEnableAutoExpressions, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 10), 0, 0));
+ panel.add(myCbEnableAlternateViews, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 10), 0, 0));
+ // starting 4-th row
+ panel.add(myCbEnableToString, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+ panel.add(myRbAllThatOverride, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 12, 0, 0), 0, 0));
+ panel.add(myRbFromList, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 12, 0, 0), 0, 0));
+ panel.add(myToStringFilterEditor, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 12, 0, 0), 0, 0));
+
+ return panel;
+ }
+
+ public void apply() {
+ final ViewsGeneralSettings generalSettings = ViewsGeneralSettings.getInstance();
+ final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
+
+ try {
+ DebuggerSettings.getInstance().VALUE_LOOKUP_DELAY = Integer.parseInt(myValueTooltipDelayField.getText().trim());
+ }
+ catch (NumberFormatException ignored) {
+ }
+ generalSettings.AUTOSCROLL_TO_NEW_LOCALS = myCbAutoscroll.isSelected();
+ rendererSettings.setAlternateCollectionViewsEnabled(myCbEnableAlternateViews.isSelected());
+ generalSettings.HIDE_NULL_ARRAY_ELEMENTS = myCbHideNullArrayElements.isSelected();
+ generalSettings.ENABLE_AUTO_EXPRESSIONS = myCbEnableAutoExpressions.isSelected();
+
+ final ClassRenderer classRenderer = rendererSettings.getClassRenderer();
+ classRenderer.SORT_ASCENDING = myCbSort.isSelected();
+ classRenderer.SHOW_STATIC = myCbShowStatic.isSelected();
+ classRenderer.SHOW_STATIC_FINAL = myCbShowStaticFinalFields.isSelectedWhenSelectable();
+ classRenderer.SHOW_SYNTHETICS = myCbShowSyntheticFields.isSelected();
+ classRenderer.SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES = myCbShowValFieldsAsLocalVariables.isSelectedWhenSelectable();
+ classRenderer.SHOW_DECLARED_TYPE = myCbShowDeclaredType.isSelected();
+ classRenderer.SHOW_FQ_TYPE_NAMES = myCbShowFQNames.isSelected();
+ classRenderer.SHOW_OBJECT_ID = myCbShowObjectId.isSelected();
+
+ final ToStringRenderer toStringRenderer = rendererSettings.getToStringRenderer();
+ toStringRenderer.setEnabled(myCbEnableToString.isSelected());
+ toStringRenderer.setUseClassFilters(myRbFromList.isSelected());
+ toStringRenderer.setClassFilters(myToStringFilterEditor.getFilters());
+
+ myAutoTooltip.save();
+
+ myArrayRendererConfigurable.apply();
+
+ rendererSettings.fireRenderersChanged();
+ }
+
+ public void reset() {
+ final ViewsGeneralSettings generalSettings = ViewsGeneralSettings.getInstance();
+ final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
+
+ myValueTooltipDelayField.setText(Integer.toString(DebuggerSettings.getInstance().VALUE_LOOKUP_DELAY));
+ myCbAutoscroll.setSelected(generalSettings.AUTOSCROLL_TO_NEW_LOCALS);
+ myCbHideNullArrayElements.setSelected(generalSettings.HIDE_NULL_ARRAY_ELEMENTS);
+ myCbEnableAlternateViews.setSelected(rendererSettings.areAlternateCollectionViewsEnabled());
+ myCbEnableAutoExpressions.setSelected(generalSettings.ENABLE_AUTO_EXPRESSIONS);
+
+ ClassRenderer classRenderer = rendererSettings.getClassRenderer();
+
+ myCbShowSyntheticFields.setSelected(classRenderer.SHOW_SYNTHETICS);
+ myCbShowValFieldsAsLocalVariables.setSelected(classRenderer.SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES);
+ if (!classRenderer.SHOW_SYNTHETICS) {
+ myCbShowValFieldsAsLocalVariables.makeUnselectable(false);
+ }
+ myCbSort.setSelected(classRenderer.SORT_ASCENDING);
+ myCbShowStatic.setSelected(classRenderer.SHOW_STATIC);
+ myCbShowStaticFinalFields.setSelected(classRenderer.SHOW_STATIC_FINAL);
+ if(!classRenderer.SHOW_STATIC) {
+ myCbShowStaticFinalFields.makeUnselectable(false);
+ }
+ myCbShowDeclaredType.setSelected(classRenderer.SHOW_DECLARED_TYPE);
+ myCbShowFQNames.setSelected(classRenderer.SHOW_FQ_TYPE_NAMES);
+ myCbShowObjectId.setSelected(classRenderer.SHOW_OBJECT_ID);
+
+ final ToStringRenderer toStringRenderer = rendererSettings.getToStringRenderer();
+ final boolean toStringEnabled = toStringRenderer.isEnabled();
+ final boolean useClassFilters = toStringRenderer.isUseClassFilters();
+ myCbEnableToString.setSelected(toStringEnabled);
+ myRbAllThatOverride.setSelected(!useClassFilters);
+ myRbFromList.setSelected(useClassFilters);
+ myToStringFilterEditor.setFilters(toStringRenderer.getClassFilters());
+ myToStringFilterEditor.setEnabled(toStringEnabled && useClassFilters);
+ myRbFromList.setEnabled(toStringEnabled);
+ myRbAllThatOverride.setEnabled(toStringEnabled);
+
+ myArrayRendererConfigurable.reset();
+ }
+
+ public boolean isModified() {
+ return areGeneralSettingsModified() || areDefaultRenderersModified() || areDebuggerSettingsModified();
+ }
+
+ private boolean areDebuggerSettingsModified() {
+ try {
+ return DebuggerSettings.getInstance().VALUE_LOOKUP_DELAY != Integer.parseInt(myValueTooltipDelayField.getText().trim());
+ }
+ catch (NumberFormatException ignored) {
+ }
+ return false;
+ }
+
+ private boolean areGeneralSettingsModified() {
+ ViewsGeneralSettings generalSettings = ViewsGeneralSettings.getInstance();
+ return
+ (generalSettings.AUTOSCROLL_TO_NEW_LOCALS != myCbAutoscroll.isSelected()) ||
+ (generalSettings.ENABLE_AUTO_EXPRESSIONS != myCbEnableAutoExpressions.isSelected()) ||
+ (generalSettings.HIDE_NULL_ARRAY_ELEMENTS != myCbHideNullArrayElements.isSelected()) || myAutoTooltip.isChanged();
+ }
+
+ private boolean areDefaultRenderersModified() {
+ if (myArrayRendererConfigurable.isModified()) {
+ return true;
+ }
+ final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
+
+ final ClassRenderer classRenderer = rendererSettings.getClassRenderer();
+ final boolean isClassRendererModified=
+ (classRenderer.SORT_ASCENDING != myCbSort.isSelected()) ||
+ (classRenderer.SHOW_STATIC != myCbShowStatic.isSelected()) ||
+ (classRenderer.SHOW_STATIC_FINAL != myCbShowStaticFinalFields.isSelectedWhenSelectable()) ||
+ (classRenderer.SHOW_SYNTHETICS != myCbShowSyntheticFields.isSelected()) ||
+ (classRenderer.SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES != myCbShowValFieldsAsLocalVariables.isSelectedWhenSelectable()) ||
+ (classRenderer.SHOW_DECLARED_TYPE != myCbShowDeclaredType.isSelected()) ||
+ (classRenderer.SHOW_FQ_TYPE_NAMES != myCbShowFQNames.isSelected()) ||
+ (classRenderer.SHOW_OBJECT_ID != myCbShowObjectId.isSelected());
+ if (isClassRendererModified) {
+ return true;
+ }
+
+ final ToStringRenderer toStringRenderer = rendererSettings.getToStringRenderer();
+ final boolean isToStringRendererModified =
+ (toStringRenderer.isEnabled() != myCbEnableToString.isSelected()) ||
+ (toStringRenderer.isUseClassFilters() != myRbFromList.isSelected()) ||
+ (!DebuggerUtilsEx.filterEquals(toStringRenderer.getClassFilters(), myToStringFilterEditor.getFilters()));
+ if (isToStringRendererModified) {
+ return true;
+ }
+
+ if (rendererSettings.areAlternateCollectionViewsEnabled() != myCbEnableAlternateViews.isSelected()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @NotNull
+ public String getHelpTopic() {
+ return "reference.idesettings.debugger.dataviews";
+ }
+
+ @NotNull
+ public String getId() {
+ return getHelpTopic();
+ }
+
+ public Runnable enableSearch(String option) {
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerHotswapConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerHotswapConfigurable.java
new file mode 100644
index 0000000..a70be30
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerHotswapConfigurable.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.options.SearchableConfigurable;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import java.awt.*;
+
+public class DebuggerHotswapConfigurable implements SearchableConfigurable {
+ private JCheckBox myHotswapInBackground;
+ private JCheckBox myCbCompileBeforeHotswap;
+ private JCheckBox myCbHangWarningEnabled;
+ private JRadioButton myRbAlways;
+ private JRadioButton myRbNever;
+ private JRadioButton myRbAsk;
+
+ public void reset() {
+ final DebuggerSettings settings = DebuggerSettings.getInstance();
+ myHotswapInBackground.setSelected(settings.HOTSWAP_IN_BACKGROUND);
+ myCbCompileBeforeHotswap.setSelected(settings.COMPILE_BEFORE_HOTSWAP);
+ myCbHangWarningEnabled.setSelected(settings.HOTSWAP_HANG_WARNING_ENABLED);
+
+ if(DebuggerSettings.RUN_HOTSWAP_ALWAYS.equals(settings.RUN_HOTSWAP_AFTER_COMPILE)) {
+ myRbAlways.setSelected(true);
+ }
+ else if(DebuggerSettings.RUN_HOTSWAP_NEVER.equals(settings.RUN_HOTSWAP_AFTER_COMPILE)) {
+ myRbNever.setSelected(true);
+ }
+ else {
+ myRbAsk.setSelected(true);
+ }
+ }
+
+ public void apply() {
+ getSettingsTo(DebuggerSettings.getInstance());
+ }
+
+ private void getSettingsTo(DebuggerSettings settings) {
+ settings.HOTSWAP_IN_BACKGROUND = myHotswapInBackground.isSelected();
+ settings.COMPILE_BEFORE_HOTSWAP = myCbCompileBeforeHotswap.isSelected();
+ settings.HOTSWAP_HANG_WARNING_ENABLED = myCbHangWarningEnabled.isSelected();
+
+ if (myRbAlways.isSelected()) {
+ settings.RUN_HOTSWAP_AFTER_COMPILE = DebuggerSettings.RUN_HOTSWAP_ALWAYS;
+ }
+ else if (myRbNever.isSelected()) {
+ settings.RUN_HOTSWAP_AFTER_COMPILE = DebuggerSettings.RUN_HOTSWAP_NEVER;
+ }
+ else {
+ settings.RUN_HOTSWAP_AFTER_COMPILE = DebuggerSettings.RUN_HOTSWAP_ASK;
+ }
+ }
+
+ public boolean isModified() {
+ final DebuggerSettings currentSettings = DebuggerSettings.getInstance();
+ final DebuggerSettings debuggerSettings = currentSettings.clone();
+ getSettingsTo(debuggerSettings);
+ return !debuggerSettings.equals(currentSettings);
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("debugger.hotswap.configurable.display.name");
+ }
+
+ public String getHelpTopic() {
+ return "reference.idesettings.debugger.hotswap";
+ }
+
+ @NotNull
+ public String getId() {
+ return getHelpTopic();
+ }
+
+ public Runnable enableSearch(String option) {
+ return null;
+ }
+
+ public JComponent createComponent() {
+ final JPanel panel = new JPanel(new GridBagLayout());
+
+ myCbCompileBeforeHotswap = new JCheckBox(DebuggerBundle.message("label.debugger.hotswap.configurable.compile.before.hotswap"));
+ myCbHangWarningEnabled = new JCheckBox(DebuggerBundle.message("label.debugger.hotswap.configurable.enable.vm.hang.warning"));
+ myHotswapInBackground = new JCheckBox(DebuggerBundle.message("label.debugger.hotswap.configurable.hotswap.background"));
+ myRbAlways = new JRadioButton(DebuggerBundle.message("label.debugger.hotswap.configurable.always"));
+ myRbNever = new JRadioButton(DebuggerBundle.message("label.debugger.hotswap.configurable.never"));
+ myRbAsk = new JRadioButton(DebuggerBundle.message("label.debugger.hotswap.configurable.ask"));
+
+ panel.add(myCbCompileBeforeHotswap, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ panel.add(myCbHangWarningEnabled, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+ panel.add(myHotswapInBackground, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+
+ int cbLeftOffset = 0;
+ final Border border = myCbCompileBeforeHotswap.getBorder();
+ if (border != null) {
+ final Insets insets = border.getBorderInsets(myCbCompileBeforeHotswap);
+ if (insets != null) {
+ cbLeftOffset = insets.left;
+ }
+ }
+
+ final ButtonGroup group = new ButtonGroup();
+ group.add(myRbAlways);
+ group.add(myRbNever);
+ group.add(myRbAsk);
+ final Box box = Box.createHorizontalBox();
+ box.add(myRbAlways);
+ box.add(myRbNever);
+ box.add(myRbAsk);
+ final JPanel reloadPanel = new JPanel(new BorderLayout());
+ reloadPanel.add(box, BorderLayout.CENTER);
+ reloadPanel.add(new JLabel(DebuggerBundle.message("label.debugger.hotswap.configurable.reload.classes")), BorderLayout.WEST);
+ panel.add(reloadPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, cbLeftOffset, 0, 0), 0, 0));
+
+ return panel;
+ }
+
+
+ public void disposeUIResources() {
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerLaunchingConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerLaunchingConfigurable.java
new file mode 100644
index 0000000..f731c89
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerLaunchingConfigurable.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.ui.StateRestoringCheckBox;
+import com.intellij.ui.components.panels.VerticalBox;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import java.awt.*;
+
+public class DebuggerLaunchingConfigurable implements Configurable{
+ private JRadioButton myRbSocket;
+ private JRadioButton myRbShmem;
+ private JCheckBox myHideDebuggerCheckBox;
+ private StateRestoringCheckBox myCbForceClassicVM;
+ private JCheckBox myCbDisableJIT;
+ private JCheckBox myFocusAppCheckBox;
+
+ public void reset() {
+ final DebuggerSettings settings = DebuggerSettings.getInstance();
+ if (!SystemInfo.isWindows) {
+ myRbSocket.setSelected(true);
+ myRbShmem.setEnabled(false);
+ }
+ else {
+ if (settings.DEBUGGER_TRANSPORT == DebuggerSettings.SHMEM_TRANSPORT) {
+ myRbShmem.setSelected(true);
+ }
+ else {
+ myRbSocket.setSelected(true);
+ }
+ myRbShmem.setEnabled(true);
+ }
+ myHideDebuggerCheckBox.setSelected(settings.HIDE_DEBUGGER_ON_PROCESS_TERMINATION);
+ myCbForceClassicVM.setSelected(settings.FORCE_CLASSIC_VM);
+ myCbDisableJIT.setSelected(settings.DISABLE_JIT);
+ myFocusAppCheckBox.setSelected(Registry.is("debugger.mayBringFrameToFrontOnBreakpoint"));
+ }
+
+ public void apply() {
+ getSettingsTo(DebuggerSettings.getInstance());
+ }
+
+ private void getSettingsTo(DebuggerSettings settings) {
+ if (myRbShmem.isSelected()) {
+ settings.DEBUGGER_TRANSPORT = DebuggerSettings.SHMEM_TRANSPORT;
+ }
+ else {
+ settings.DEBUGGER_TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT;
+ }
+ settings.HIDE_DEBUGGER_ON_PROCESS_TERMINATION = myHideDebuggerCheckBox.isSelected();
+ settings.FORCE_CLASSIC_VM = myCbForceClassicVM.isSelectedWhenSelectable();
+ settings.DISABLE_JIT = myCbDisableJIT.isSelected();
+ Registry.get("debugger.mayBringFrameToFrontOnBreakpoint").setValue(myFocusAppCheckBox.isSelected());
+ }
+
+ public boolean isModified() {
+ final DebuggerSettings currentSettings = DebuggerSettings.getInstance();
+ final DebuggerSettings debuggerSettings = currentSettings.clone();
+ getSettingsTo(debuggerSettings);
+ return !debuggerSettings.equals(currentSettings) || Registry.is("debugger.mayBringFrameToFrontOnBreakpoint") != myFocusAppCheckBox.isSelected();
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("debugger.launching.configurable.display.name");
+ }
+
+ public String getHelpTopic() {
+ return "reference.idesettings.debugger.launching";
+ }
+
+ public JComponent createComponent() {
+ myCbForceClassicVM = new StateRestoringCheckBox(DebuggerBundle.message("label.debugger.launching.configurable.force.classic.vm"));
+ myCbDisableJIT = new JCheckBox(DebuggerBundle.message("label.debugger.launching.configurable.disable.jit"));
+ myHideDebuggerCheckBox = new JCheckBox(DebuggerBundle.message("label.debugger.launching.configurable.hide.window"));
+ myRbSocket = new JRadioButton(DebuggerBundle.message("label.debugger.launching.configurable.socket"));
+ myRbShmem = new JRadioButton(DebuggerBundle.message("label.debugger.launching.configurable.shmem"));
+ myFocusAppCheckBox = new JCheckBox(DebuggerBundle.message("label.debugger.focusAppOnBreakpoint"));
+
+ int cbLeftOffset = 0;
+ final Border border = myCbForceClassicVM.getBorder();
+ if (border != null) {
+ final Insets insets = border.getBorderInsets(myCbForceClassicVM);
+ if (insets != null) {
+ cbLeftOffset = insets.left;
+ }
+ }
+
+ final ButtonGroup gr = new ButtonGroup();
+ gr.add(myRbSocket);
+ gr.add(myRbShmem);
+ final Box box = Box.createHorizontalBox();
+ box.add(myRbSocket);
+ box.add(myRbShmem);
+ final JPanel transportPanel = new JPanel(new BorderLayout());
+ transportPanel.add(new JLabel(DebuggerBundle.message("label.debugger.launching.configurable.debugger.transport")), BorderLayout.WEST);
+ transportPanel.add(box, BorderLayout.CENTER);
+
+ VerticalBox panel = new VerticalBox();
+ panel.setOpaque(false);
+ panel.add(transportPanel);
+ panel.add(myCbForceClassicVM);
+ panel.add(myCbDisableJIT);
+ panel.add(myHideDebuggerCheckBox);
+ panel.add(myFocusAppCheckBox);
+
+ JPanel result = new JPanel(new BorderLayout());
+ result.add(panel, BorderLayout.NORTH);
+
+ return result;
+ }
+
+
+ public void disposeUIResources() {
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java
new file mode 100644
index 0000000..274fff6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java
@@ -0,0 +1,274 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.openapi.components.NamedComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.ui.classFilter.ClassFilter;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DebuggerSettings implements JDOMExternalizable, NamedComponent, Cloneable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.settings.DebuggerSettings");
+ public static final int SOCKET_TRANSPORT = 0;
+ public static final int SHMEM_TRANSPORT = 1;
+
+ @NonNls public static final String SUSPEND_ALL = "SuspendAll";
+ @NonNls public static final String SUSPEND_THREAD = "SuspendThread";
+ @NonNls public static final String SUSPEND_NONE = "SuspendNone";
+
+ @NonNls public static final String EVALUATE_FRAGMENT = "EvaluateFragment";
+ @NonNls public static final String EVALUATE_EXPRESSION = "EvaluateExpression";
+
+ @NonNls public static final String RUN_HOTSWAP_ALWAYS = "RunHotswapAlways";
+ @NonNls public static final String RUN_HOTSWAP_NEVER = "RunHotswapNever";
+ @NonNls public static final String RUN_HOTSWAP_ASK = "RunHotswapAsk";
+
+ public boolean TRACING_FILTERS_ENABLED;
+ public int VALUE_LOOKUP_DELAY; // ms
+ public int DEBUGGER_TRANSPORT;
+ public boolean FORCE_CLASSIC_VM;
+ public boolean DISABLE_JIT;
+ public boolean HIDE_DEBUGGER_ON_PROCESS_TERMINATION;
+ public boolean HOTSWAP_IN_BACKGROUND = true;
+ public boolean SKIP_SYNTHETIC_METHODS;
+ public boolean SKIP_CONSTRUCTORS;
+ public boolean SKIP_GETTERS;
+ public boolean SKIP_CLASSLOADERS;
+
+ public String EVALUATION_DIALOG_TYPE;
+ public String RUN_HOTSWAP_AFTER_COMPILE;
+ public boolean COMPILE_BEFORE_HOTSWAP;
+ public boolean HOTSWAP_HANG_WARNING_ENABLED = true;
+
+ public volatile boolean WATCH_RETURN_VALUES = false;
+ public volatile boolean AUTO_VARIABLES_MODE = false;
+ public volatile boolean SHOW_LIBRARY_STACKFRAMES = true;
+
+
+ private ClassFilter[] mySteppingFilters = ClassFilter.EMPTY_ARRAY;
+
+ private Map<String, ContentState> myContentStates = new HashMap<String, ContentState>();
+
+ public ClassFilter[] getSteppingFilters() {
+ final ClassFilter[] rv = new ClassFilter[mySteppingFilters.length];
+ for (int idx = 0; idx < rv.length; idx++) {
+ rv[idx] = mySteppingFilters[idx].clone();
+ }
+ return rv;
+ }
+
+ void setSteppingFilters(ClassFilter[] steppingFilters) {
+ mySteppingFilters = steppingFilters != null ? steppingFilters : ClassFilter.EMPTY_ARRAY;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void readExternal(Element parentNode) throws InvalidDataException {
+ DefaultJDOMExternalizer.readExternal(this, parentNode);
+ List<ClassFilter> filtersList = new ArrayList<ClassFilter>();
+
+ for (final Object o : parentNode.getChildren("filter")) {
+ Element filter = (Element)o;
+ filtersList.add(DebuggerUtilsEx.create(filter));
+ }
+ setSteppingFilters(filtersList.toArray(new ClassFilter[filtersList.size()]));
+
+ filtersList.clear();
+
+ final List contents = parentNode.getChildren("content");
+ myContentStates.clear();
+ for (Object content : contents) {
+ final ContentState state = new ContentState((Element)content);
+ myContentStates.put(state.getType(), state);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(Element parentNode) throws WriteExternalException {
+ DefaultJDOMExternalizer.writeExternal(this, parentNode);
+ for (ClassFilter mySteppingFilter : mySteppingFilters) {
+ Element element = new Element("filter");
+ parentNode.addContent(element);
+ mySteppingFilter.writeExternal(element);
+ }
+
+ for (ContentState eachState : myContentStates.values()) {
+ final Element content = new Element("content");
+ if (eachState.write(content)) {
+ parentNode.addContent(content);
+ }
+ }
+ }
+
+ public static DebuggerSettings getInstance() {
+ return ServiceManager.getService(DebuggerSettings.class);
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DebuggerSettings)) return false;
+ DebuggerSettings secondSettings = (DebuggerSettings)obj;
+
+ return
+ TRACING_FILTERS_ENABLED == secondSettings.TRACING_FILTERS_ENABLED &&
+ VALUE_LOOKUP_DELAY == secondSettings.VALUE_LOOKUP_DELAY &&
+ DEBUGGER_TRANSPORT == secondSettings.DEBUGGER_TRANSPORT &&
+ FORCE_CLASSIC_VM == secondSettings.FORCE_CLASSIC_VM &&
+ DISABLE_JIT == secondSettings.DISABLE_JIT &&
+ HIDE_DEBUGGER_ON_PROCESS_TERMINATION == secondSettings.HIDE_DEBUGGER_ON_PROCESS_TERMINATION &&
+ HOTSWAP_IN_BACKGROUND == secondSettings.HOTSWAP_IN_BACKGROUND &&
+ SKIP_SYNTHETIC_METHODS == secondSettings.SKIP_SYNTHETIC_METHODS &&
+ SKIP_CLASSLOADERS == secondSettings.SKIP_CLASSLOADERS &&
+ SKIP_CONSTRUCTORS == secondSettings.SKIP_CONSTRUCTORS &&
+ SKIP_GETTERS == secondSettings.SKIP_GETTERS &&
+ COMPILE_BEFORE_HOTSWAP == secondSettings.COMPILE_BEFORE_HOTSWAP &&
+ HOTSWAP_HANG_WARNING_ENABLED == secondSettings.HOTSWAP_HANG_WARNING_ENABLED &&
+ (RUN_HOTSWAP_AFTER_COMPILE != null ? RUN_HOTSWAP_AFTER_COMPILE.equals(secondSettings.RUN_HOTSWAP_AFTER_COMPILE) : secondSettings.RUN_HOTSWAP_AFTER_COMPILE == null) &&
+ DebuggerUtilsEx.filterEquals(mySteppingFilters, secondSettings.mySteppingFilters);
+ }
+
+ public DebuggerSettings clone() {
+ try {
+ final DebuggerSettings cloned = (DebuggerSettings)super.clone();
+ cloned.myContentStates = new HashMap<String, ContentState>();
+ for (Map.Entry<String, ContentState> entry : myContentStates.entrySet()) {
+ cloned.myContentStates.put(entry.getKey(), entry.getValue().clone());
+ }
+ cloned.mySteppingFilters = new ClassFilter[mySteppingFilters.length];
+ for (int idx = 0; idx < mySteppingFilters.length; idx++) {
+ cloned.mySteppingFilters[idx] = mySteppingFilters[idx].clone();
+ }
+ return cloned;
+ }
+ catch (CloneNotSupportedException e) {
+ LOG.error(e);
+ }
+ return null;
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "DebuggerSettings";
+ }
+
+ public static class ContentState implements Cloneable {
+
+ private final String myType;
+ private boolean myMinimized;
+ private String mySelectedTab;
+ private double mySplitProportion;
+ private boolean myDetached;
+ private boolean myHorizontalToolbar;
+ private boolean myMaximized;
+
+ public ContentState(final String type) {
+ myType = type;
+ }
+
+ public ContentState(Element element) {
+ myType = element.getAttributeValue("type");
+ myMinimized = "true".equalsIgnoreCase(element.getAttributeValue("minimized"));
+ myMaximized = "true".equalsIgnoreCase(element.getAttributeValue("maximized"));
+ mySelectedTab = element.getAttributeValue("selected");
+ final String split = element.getAttributeValue("split");
+ if (split != null) {
+ mySplitProportion = Double.valueOf(split);
+ }
+ myDetached = "true".equalsIgnoreCase(element.getAttributeValue("detached"));
+ myHorizontalToolbar = !"false".equalsIgnoreCase(element.getAttributeValue("horizontal"));
+ }
+
+ public boolean write(final Element element) {
+ element.setAttribute("type", myType);
+ element.setAttribute("minimized", Boolean.valueOf(myMinimized).toString());
+ element.setAttribute("maximized", Boolean.valueOf(myMaximized).toString());
+ if (mySelectedTab != null) {
+ element.setAttribute("selected", mySelectedTab);
+ }
+ element.setAttribute("split", new Double(mySplitProportion).toString());
+ element.setAttribute("detached", Boolean.valueOf(myDetached).toString());
+ element.setAttribute("horizontal", Boolean.valueOf(myHorizontalToolbar).toString());
+ return true;
+ }
+
+ public String getType() {
+ return myType;
+ }
+
+ public String getSelectedTab() {
+ return mySelectedTab;
+ }
+
+ public boolean isMinimized() {
+ return myMinimized;
+ }
+
+ public void setMinimized(final boolean minimized) {
+ myMinimized = minimized;
+ }
+
+ public void setMaximized(final boolean maximized) {
+ myMaximized = maximized;
+ }
+
+ public boolean isMaximized() {
+ return myMaximized;
+ }
+
+ public void setSelectedTab(final String selectedTab) {
+ mySelectedTab = selectedTab;
+ }
+
+ public void setSplitProportion(double splitProportion) {
+ mySplitProportion = splitProportion;
+ }
+
+ public double getSplitProportion(double defaultValue) {
+ return mySplitProportion <= 0 || mySplitProportion >= 1 ? defaultValue : mySplitProportion;
+ }
+
+ public void setDetached(final boolean detached) {
+ myDetached = detached;
+ }
+
+ public boolean isDetached() {
+ return myDetached;
+ }
+
+ public boolean isHorizontalToolbar() {
+ return myHorizontalToolbar;
+ }
+
+ public void setHorizontalToolbar(final boolean horizontalToolbar) {
+ myHorizontalToolbar = horizontalToolbar;
+ }
+
+ public ContentState clone() throws CloneNotSupportedException {
+ return (ContentState)super.clone();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.java
new file mode 100644
index 0000000..d85e636
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.classFilter.ClassFilterEditor;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class DebuggerSteppingConfigurable implements SearchableConfigurable, Configurable.NoScroll {
+ private JCheckBox myCbStepInfoFiltersEnabled;
+ private JCheckBox myCbSkipSyntheticMethods;
+ private JCheckBox myCbSkipConstructors;
+ private JCheckBox myCbSkipClassLoaders;
+ private ClassFilterEditor mySteppingFilterEditor;
+ private JCheckBox myCbSkipSimpleGetters;
+ private Project myProject;
+
+ public void reset() {
+ final DebuggerSettings settings = DebuggerSettings.getInstance();
+ myCbSkipSimpleGetters.setSelected(settings.SKIP_GETTERS);
+ myCbSkipSyntheticMethods.setSelected(settings.SKIP_SYNTHETIC_METHODS);
+ myCbSkipConstructors.setSelected(settings.SKIP_CONSTRUCTORS);
+ myCbSkipClassLoaders.setSelected(settings.SKIP_CLASSLOADERS);
+
+ myCbStepInfoFiltersEnabled.setSelected(settings.TRACING_FILTERS_ENABLED);
+
+ mySteppingFilterEditor.setFilters(settings.getSteppingFilters());
+ mySteppingFilterEditor.setEnabled(settings.TRACING_FILTERS_ENABLED);
+
+
+ }
+
+ public void apply() {
+ getSettingsTo(DebuggerSettings.getInstance());
+ }
+
+ private void getSettingsTo(DebuggerSettings settings) {
+ settings.SKIP_GETTERS = myCbSkipSimpleGetters.isSelected();
+ settings.SKIP_SYNTHETIC_METHODS = myCbSkipSyntheticMethods.isSelected();
+ settings.SKIP_CONSTRUCTORS = myCbSkipConstructors.isSelected();
+ settings.SKIP_CLASSLOADERS = myCbSkipClassLoaders.isSelected();
+ settings.TRACING_FILTERS_ENABLED = myCbStepInfoFiltersEnabled.isSelected();
+
+ mySteppingFilterEditor.stopEditing();
+ settings.setSteppingFilters(mySteppingFilterEditor.getFilters());
+ }
+
+ public boolean isModified() {
+ final DebuggerSettings currentSettings = DebuggerSettings.getInstance();
+ final DebuggerSettings debuggerSettings = currentSettings.clone();
+ getSettingsTo(debuggerSettings);
+ return !debuggerSettings.equals(currentSettings);
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("debugger.stepping.configurable.display.name");
+ }
+
+ @NotNull
+ public String getHelpTopic() {
+ return "reference.idesettings.debugger.stepping";
+ }
+
+ @NotNull
+ public String getId() {
+ return getHelpTopic();
+ }
+
+ public Runnable enableSearch(String option) {
+ return null;
+ }
+
+ public JComponent createComponent() {
+ final JPanel panel = new JPanel(new GridBagLayout());
+ myProject = JavaDebuggerSupport.getCurrentProject();
+ myCbSkipSyntheticMethods = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.synthetic.methods"));
+ myCbSkipConstructors = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.constructors"));
+ myCbSkipClassLoaders = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.classloaders"));
+ myCbSkipSimpleGetters = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.simple.getters"));
+ myCbStepInfoFiltersEnabled = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.step.filters.list.header"));
+ panel.add(myCbSkipSyntheticMethods, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
+ panel.add(myCbSkipConstructors, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
+ panel.add(myCbSkipClassLoaders, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
+ panel.add(myCbSkipSimpleGetters, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
+ panel.add(myCbStepInfoFiltersEnabled, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(8, 0, 0, 0),0, 0));
+
+ mySteppingFilterEditor = new ClassFilterEditor(myProject, null, "reference.viewBreakpoints.classFilters.newPattern");
+ panel.add(mySteppingFilterEditor, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 5, 0, 0),0, 0));
+
+ myCbStepInfoFiltersEnabled.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ mySteppingFilterEditor.setEnabled(myCbStepInfoFiltersEnabled.isSelected());
+ }
+ });
+ return panel;
+ }
+
+ public void disposeUIResources() {
+ mySteppingFilterEditor = null;
+ myProject = null;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/FieldDataBinding.java b/java/debugger/impl/src/com/intellij/debugger/settings/FieldDataBinding.java
new file mode 100644
index 0000000..8473fbf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/FieldDataBinding.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import org.jetbrains.annotations.NonNls;
+
+import java.lang.reflect.Field;
+
+import com.intellij.debugger.DebuggerBundle;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 12, 2005
+ */
+public abstract class FieldDataBinding implements DataBinding{
+
+ private final String myFieldName;
+
+ protected FieldDataBinding(@NonNls String fieldName) {
+ myFieldName = fieldName;
+ }
+
+ public final void loadData(Object from) {
+ try {
+ final Field field = findField(from);
+ doLoadData(from, field);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+
+ public final void saveData(Object to) {
+ try {
+ final Field field = findField(to);
+ doSaveData(to, field);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public boolean isModified(Object obj) {
+ try {
+ final Field field = findField(obj);
+ return isModified(obj, field);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ protected abstract void doLoadData(Object from, Field field) throws IllegalAccessException;
+
+ protected abstract void doSaveData(Object to, Field field) throws IllegalAccessException;
+
+ protected abstract boolean isModified(Object obj, Field field) throws IllegalAccessException;
+
+ private Field findField(Object from) {
+ final Class objectClass = Object.class;
+ for (Class aClass = from.getClass(); !aClass.equals(objectClass); aClass = aClass.getSuperclass()) {
+ try {
+ final Field field = aClass.getDeclaredField(myFieldName);
+ field.setAccessible(true);
+ return field;
+ }
+ catch (NoSuchFieldException e) {
+ // ignored, just continue
+ }
+ }
+ throw new RuntimeException(DebuggerBundle.message("error.field.not.found.in.class", myFieldName, from.getClass().getName()));
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java
new file mode 100644
index 0000000..d64c783
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.debugger.ui.tree.render.*;
+import com.intellij.debugger.ui.tree.render.Renderer;
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.psi.CommonClassNames;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.containers.InternalIterator;
+import com.intellij.util.ui.ColorIcon;
+import com.sun.jdi.*;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 18, 2003
+ * Time: 8:00:25 PM
+ */
+@State(
+ name="NodeRendererSettings",
+ storages= {
+ @Storage(
+ file = StoragePathMacros.APP_CONFIG + "/debugger.renderers.xml"
+ )}
+)
+public class NodeRendererSettings implements PersistentStateComponent<Element> {
+ @NonNls private static final String REFERENCE_RENDERER = "Reference renderer";
+ @NonNls public static final String RENDERER_TAG = "Renderer";
+ @NonNls private static final String RENDERER_ID = "ID";
+
+ private final EventDispatcher<NodeRendererSettingsListener> myDispatcher = EventDispatcher.create(NodeRendererSettingsListener.class);
+ private final List<NodeRenderer> myPluginRenderers = new ArrayList<NodeRenderer>();
+ private RendererConfiguration myCustomRenderers = new RendererConfiguration(this);
+
+ // base renderers
+ private final PrimitiveRenderer myPrimitiveRenderer = new PrimitiveRenderer();
+ private final ArrayRenderer myArrayRenderer = new ArrayRenderer();
+ private final ClassRenderer myClassRenderer = new ClassRenderer();
+ private final HexRenderer myHexRenderer = new HexRenderer();
+ private final ToStringRenderer myToStringRenderer = new ToStringRenderer();
+ private final CompoundReferenceRenderer myColorRenderer;
+ // alternate collections
+ private final NodeRenderer[] myAlternateCollectionRenderers = new NodeRenderer[]{
+ createCompoundReferenceRenderer(
+ "Map", CommonClassNames.JAVA_UTIL_MAP,
+ createLabelRenderer(" size = ", "size()", null),
+ createExpressionChildrenRenderer("entrySet().toArray()", "!isEmpty()")
+ ),
+ createCompoundReferenceRenderer(
+ "Map.Entry", "java.util.Map$Entry",
+ new MapEntryLabelRenderer()/*createLabelRenderer(null, "\" \" + getKey() + \" -> \" + getValue()", null)*/,
+ createEnumerationChildrenRenderer(new String[][]{{"key", "getKey()"}, {"value", "getValue()"}})
+ ),
+ createCompoundReferenceRenderer(
+ "Collection", "java.util.Collection",
+ createLabelRenderer(" size = ", "size()", null),
+ createExpressionChildrenRenderer("toArray()", "!isEmpty()")
+ )
+ };
+ @NonNls private static final String HEX_VIEW_ENABLED = "HEX_VIEW_ENABLED";
+ @NonNls private static final String ALTERNATIVE_COLLECTION_VIEW_ENABLED = "ALTERNATIVE_COLLECTION_VIEW_ENABLED";
+ @NonNls private static final String CUSTOM_RENDERERS_TAG_NAME = "CustomRenderers";
+
+ public NodeRendererSettings() {
+ myColorRenderer = new ColorObjectRenderer(this);
+ // default configuration
+ myHexRenderer.setEnabled(false);
+ myToStringRenderer.setEnabled(true);
+ setAlternateCollectionViewsEnabled(true);
+ myColorRenderer.setEnabled(true);
+ }
+
+ public static NodeRendererSettings getInstance() {
+ return ServiceManager.getService(NodeRendererSettings.class);
+ }
+
+ public void addPluginRenderer(NodeRenderer renderer) {
+ myPluginRenderers.add(renderer);
+ }
+
+ public void removePluginRenderer(NodeRenderer renderer) {
+ myPluginRenderers.remove(renderer);
+ }
+
+ public void setAlternateCollectionViewsEnabled(boolean enabled) {
+ for (NodeRenderer myAlternateCollectionRenderer : myAlternateCollectionRenderers) {
+ myAlternateCollectionRenderer.setEnabled(enabled);
+ }
+ }
+
+ public boolean areAlternateCollectionViewsEnabled() {
+ return myAlternateCollectionRenderers[0].isEnabled();
+ }
+
+ public boolean equals(Object o) {
+ if(!(o instanceof NodeRendererSettings)) return false;
+
+ return DebuggerUtilsEx.elementsEqual(getState(), ((NodeRendererSettings)o).getState());
+ }
+
+ public void addListener(NodeRendererSettingsListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ public void removeListener(NodeRendererSettingsListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public Element getState() {
+ final Element element = new Element("NodeRendererSettings");
+ JDOMExternalizerUtil.writeField(element, HEX_VIEW_ENABLED, myHexRenderer.isEnabled()? "true" : "false");
+ JDOMExternalizerUtil.writeField(element, ALTERNATIVE_COLLECTION_VIEW_ENABLED, areAlternateCollectionViewsEnabled()? "true" : "false");
+ try {
+ element.addContent(writeRenderer(myArrayRenderer));
+ element.addContent(writeRenderer(myToStringRenderer));
+ element.addContent(writeRenderer(myClassRenderer));
+ if (myCustomRenderers.getRendererCount() > 0) {
+ final Element custom = new Element(CUSTOM_RENDERERS_TAG_NAME);
+ element.addContent(custom);
+ myCustomRenderers.writeExternal(custom);
+ }
+ }
+ catch (WriteExternalException e) {
+ // ignore
+ }
+ return element;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void loadState(final Element root) {
+ final String hexEnabled = JDOMExternalizerUtil.readField(root, HEX_VIEW_ENABLED);
+ if (hexEnabled != null) {
+ myHexRenderer.setEnabled("true".equalsIgnoreCase(hexEnabled));
+ }
+
+ final String alternativeEnabled = JDOMExternalizerUtil.readField(root, ALTERNATIVE_COLLECTION_VIEW_ENABLED);
+ if (alternativeEnabled != null) {
+ setAlternateCollectionViewsEnabled("true".equalsIgnoreCase(alternativeEnabled));
+ }
+
+ final List rendererElements = root.getChildren(RENDERER_TAG);
+ for (final Object rendererElement : rendererElements) {
+ final Element elem = (Element)rendererElement;
+ final String id = elem.getAttributeValue(RENDERER_ID);
+ if (id == null) {
+ continue;
+ }
+ try {
+ if (ArrayRenderer.UNIQUE_ID.equals(id)) {
+ myArrayRenderer.readExternal(elem);
+ }
+ else if (ToStringRenderer.UNIQUE_ID.equals(id)) {
+ myToStringRenderer.readExternal(elem);
+ }
+ else if (ClassRenderer.UNIQUE_ID.equals(id)) {
+ myClassRenderer.readExternal(elem);
+ }
+ }
+ catch (InvalidDataException e) {
+ // ignore
+ }
+ }
+ final Element custom = root.getChild(CUSTOM_RENDERERS_TAG_NAME);
+ if (custom != null) {
+ myCustomRenderers.readExternal(custom);
+ }
+
+ myDispatcher.getMulticaster().renderersChanged();
+ }
+
+ public RendererConfiguration getCustomRenderers() {
+ return myCustomRenderers;
+ }
+
+ public void setCustomRenderers(@NotNull final RendererConfiguration customRenderers) {
+ RendererConfiguration oldConfig = myCustomRenderers;
+ myCustomRenderers = customRenderers;
+ if (oldConfig == null || !oldConfig.equals(customRenderers)) {
+ fireRenderersChanged();
+ }
+ }
+
+ public List<NodeRenderer> getPluginRenderers() {
+ return new ArrayList<NodeRenderer>(myPluginRenderers);
+ }
+
+ public PrimitiveRenderer getPrimitiveRenderer() {
+ return myPrimitiveRenderer;
+ }
+
+ public ArrayRenderer getArrayRenderer() {
+ return myArrayRenderer;
+ }
+
+ public ClassRenderer getClassRenderer() {
+ return myClassRenderer;
+ }
+
+ public HexRenderer getHexRenderer() {
+ return myHexRenderer;
+ }
+
+ public ToStringRenderer getToStringRenderer() {
+ return myToStringRenderer;
+ }
+
+ public NodeRenderer[] getAlternateCollectionRenderers() {
+ return myAlternateCollectionRenderers;
+ }
+
+ public void fireRenderersChanged() {
+ myDispatcher.getMulticaster().renderersChanged();
+ }
+
+ public List<NodeRenderer> getAllRenderers() {
+ // the order is important as the renderers are applied according to it
+ final List<NodeRenderer> allRenderers = new ArrayList<NodeRenderer>();
+ allRenderers.add(myHexRenderer);
+ allRenderers.add(myPrimitiveRenderer);
+ allRenderers.addAll(myPluginRenderers);
+ myCustomRenderers.iterateRenderers(new InternalIterator<NodeRenderer>() {
+ public boolean visit(final NodeRenderer renderer) {
+ allRenderers.add(renderer);
+ return true;
+ }
+ });
+ for (NodeRenderer myAlternateCollectionRenderer : myAlternateCollectionRenderers) {
+ allRenderers.add(myAlternateCollectionRenderer);
+ }
+ allRenderers.add(myColorRenderer);
+ allRenderers.add(myToStringRenderer);
+ allRenderers.add(myArrayRenderer);
+ allRenderers.add(myClassRenderer);
+ return allRenderers;
+ }
+
+ public boolean isBase(final Renderer renderer) {
+ return renderer == myPrimitiveRenderer || renderer == myArrayRenderer || renderer == myClassRenderer;
+ }
+
+ public Renderer readRenderer(Element root) throws InvalidDataException {
+ if (root == null) {
+ return null;
+ }
+
+ if (!RENDERER_TAG.equals(root.getName())) {
+ throw new InvalidDataException("Cannot read renderer - tag name is not '" + RENDERER_TAG + "'");
+ }
+
+ final String rendererId = root.getAttributeValue(RENDERER_ID);
+ if(rendererId == null) {
+ throw new InvalidDataException("unknown renderer ID: " + rendererId);
+ }
+
+ final Renderer renderer = createRenderer(rendererId);
+ if(renderer == null) {
+ throw new InvalidDataException("unknown renderer ID: " + rendererId);
+ }
+
+ renderer.readExternal(root);
+
+ return renderer;
+ }
+
+ public Element writeRenderer(Renderer renderer) throws WriteExternalException {
+ Element root = new Element(RENDERER_TAG);
+ if(renderer != null) {
+ root.setAttribute(RENDERER_ID , renderer.getUniqueId());
+ renderer.writeExternal(root);
+ }
+ return root;
+ }
+
+ public Renderer createRenderer(final String rendererId) {
+ if (ClassRenderer.UNIQUE_ID.equals(rendererId)) {
+ return myClassRenderer;
+ }
+ else if (ArrayRenderer.UNIQUE_ID.equals(rendererId)) {
+ return myArrayRenderer;
+ }
+ else if (PrimitiveRenderer.UNIQUE_ID.equals(rendererId)) {
+ return myPrimitiveRenderer;
+ }
+ else if(HexRenderer.UNIQUE_ID.equals(rendererId)) {
+ return myHexRenderer;
+ }
+ else if(rendererId.equals(ExpressionChildrenRenderer.UNIQUE_ID)) {
+ return new ExpressionChildrenRenderer();
+ }
+ else if(rendererId.equals(LabelRenderer.UNIQUE_ID)) {
+ return new LabelRenderer();
+ }
+ else if(rendererId.equals(EnumerationChildrenRenderer.UNIQUE_ID)) {
+ return new EnumerationChildrenRenderer();
+ }
+ else if(rendererId.equals(ToStringRenderer.UNIQUE_ID)) {
+ return myToStringRenderer;
+ }
+ else if(rendererId.equals(CompoundNodeRenderer.UNIQUE_ID) || rendererId.equals(REFERENCE_RENDERER)) {
+ return createCompoundReferenceRenderer("unnamed", CommonClassNames.JAVA_LANG_OBJECT, null, null);
+ }
+ return null;
+ }
+
+ private CompoundReferenceRenderer createCompoundReferenceRenderer(
+ @NonNls final String rendererName, @NonNls final String className, final ValueLabelRenderer labelRenderer, final ChildrenRenderer childrenRenderer
+ ) {
+ CompoundReferenceRenderer renderer = new CompoundReferenceRenderer(this, rendererName, labelRenderer, childrenRenderer);
+ renderer.setClassName(className);
+ return renderer;
+ }
+
+ private ExpressionChildrenRenderer createExpressionChildrenRenderer(@NonNls String expressionText, @NonNls String childrenExpandableText) {
+ final ExpressionChildrenRenderer childrenRenderer = new ExpressionChildrenRenderer();
+ childrenRenderer.setChildrenExpression(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expressionText, "", StdFileTypes.JAVA));
+ if (childrenExpandableText != null) {
+ childrenRenderer.setChildrenExpandable(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, childrenExpandableText, "", StdFileTypes.JAVA));
+ }
+ return childrenRenderer;
+ }
+
+ private EnumerationChildrenRenderer createEnumerationChildrenRenderer(@NonNls String[][] expressions) {
+ final EnumerationChildrenRenderer childrenRenderer = new EnumerationChildrenRenderer();
+ if (expressions != null && expressions.length > 0) {
+ final ArrayList<Pair<String, TextWithImports>> childrenList = new ArrayList<Pair<String, TextWithImports>>(expressions.length);
+ for (final String[] expression : expressions) {
+ childrenList.add(new Pair<String, TextWithImports>(expression[0], new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression[1], "", StdFileTypes.JAVA)));
+ }
+ childrenRenderer.setChildren(childrenList);
+ }
+ return childrenRenderer;
+ }
+
+ private static LabelRenderer createLabelRenderer(@NonNls final String prefix, @NonNls final String expressionText, @NonNls final String postfix) {
+ final LabelRenderer labelRenderer = new LabelRenderer() {
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener) throws EvaluateException {
+ final String evaluated = super.calcLabel(descriptor, evaluationContext, labelListener);
+ if (prefix == null && postfix == null) {
+ return evaluated;
+ }
+ if (prefix != null && postfix != null) {
+ return prefix + evaluated + postfix;
+ }
+ if (prefix != null) {
+ return prefix + evaluated;
+ }
+ return evaluated + postfix;
+ }
+ };
+ labelRenderer.setLabelExpression(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expressionText, "", StdFileTypes.JAVA));
+ return labelRenderer;
+ }
+
+ private static class MapEntryLabelRenderer extends ReferenceRenderer implements ValueLabelRenderer{
+ private static final Computable<String> NULL_LABEL_COMPUTABLE = new Computable<String>() {
+ public String compute() {
+ return "null";
+ }
+ };
+
+ private final MyCachedEvaluator myKeyExpression = new MyCachedEvaluator();
+ private final MyCachedEvaluator myValueExpression = new MyCachedEvaluator();
+
+ private MapEntryLabelRenderer() {
+ super("java.util.Map$Entry");
+ myKeyExpression.setReferenceExpression(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "this.getKey()", "", StdFileTypes.JAVA));
+ myValueExpression.setReferenceExpression(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "this.getValue()", "", StdFileTypes.JAVA));
+ }
+
+ public Icon calcValueIcon(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ return null;
+ }
+
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ final DescriptorUpdater descriptorUpdater = new DescriptorUpdater(descriptor, listener);
+
+ final Value originalValue = descriptor.getValue();
+ final Pair<Computable<String>, ValueDescriptorImpl> keyPair = createValueComputable(evaluationContext, originalValue, myKeyExpression, descriptorUpdater);
+ final Pair<Computable<String>, ValueDescriptorImpl> valuePair = createValueComputable(evaluationContext, originalValue, myValueExpression, descriptorUpdater);
+
+ descriptorUpdater.setKeyDescriptor(keyPair.second);
+ descriptorUpdater.setValueDescriptor(valuePair.second);
+
+ return DescriptorUpdater.constructLabelText(keyPair.first.compute(), valuePair.first.compute());
+ }
+
+ private Pair<Computable<String>, ValueDescriptorImpl> createValueComputable(final EvaluationContext evaluationContext,
+ Value originalValue,
+ final MyCachedEvaluator evaluator,
+ final DescriptorLabelListener listener) throws EvaluateException {
+ final Value eval = doEval(evaluationContext, originalValue, evaluator);
+ if (eval != null) {
+ final WatchItemDescriptor evalDescriptor = new WatchItemDescriptor(evaluationContext.getProject(), evaluator.getReferenceExpression(), eval);
+ evalDescriptor.setShowIdLabel(false);
+ return new Pair<Computable<String>, ValueDescriptorImpl>(new Computable<String>() {
+ public String compute() {
+ evalDescriptor.updateRepresentation((EvaluationContextImpl)evaluationContext, listener);
+ return evalDescriptor.getValueLabel();
+ }
+ }, evalDescriptor);
+ }
+ return new Pair<Computable<String>, ValueDescriptorImpl>(NULL_LABEL_COMPUTABLE, null);
+ }
+
+ public String getUniqueId() {
+ return "MapEntry renderer";
+ }
+
+ private Value doEval(EvaluationContext evaluationContext, Value originalValue, MyCachedEvaluator cachedEvaluator)
+ throws EvaluateException {
+ final DebugProcess debugProcess = evaluationContext.getDebugProcess();
+ if (originalValue == null) {
+ return null;
+ }
+ try {
+ final ExpressionEvaluator evaluator = cachedEvaluator.getEvaluator(debugProcess.getProject());
+ if(!debugProcess.isAttached()) {
+ throw EvaluateExceptionUtil.PROCESS_EXITED;
+ }
+ final EvaluationContext thisEvaluationContext = evaluationContext.createEvaluationContext(originalValue);
+ return evaluator.evaluate(thisEvaluationContext);
+ }
+ catch (final EvaluateException ex) {
+ throw new EvaluateException(DebuggerBundle.message("error.unable.to.evaluate.expression") + " " + ex.getMessage(), ex);
+ }
+ }
+
+ private class MyCachedEvaluator extends CachedEvaluator {
+ protected String getClassName() {
+ return MapEntryLabelRenderer.this.getClassName();
+ }
+
+ public ExpressionEvaluator getEvaluator(Project project) throws EvaluateException {
+ return super.getEvaluator(project);
+ }
+ }
+ }
+
+ private static class DescriptorUpdater implements DescriptorLabelListener {
+ private final ValueDescriptor myTargetDescriptor;
+ @Nullable
+ private ValueDescriptorImpl myKeyDescriptor;
+ @Nullable
+ private ValueDescriptorImpl myValueDescriptor;
+ private final DescriptorLabelListener myDelegate;
+
+ private DescriptorUpdater(ValueDescriptor descriptor, DescriptorLabelListener delegate) {
+ myTargetDescriptor = descriptor;
+ myDelegate = delegate;
+ }
+
+ public void setKeyDescriptor(@Nullable ValueDescriptorImpl keyDescriptor) {
+ myKeyDescriptor = keyDescriptor;
+ }
+
+ public void setValueDescriptor(@Nullable ValueDescriptorImpl valueDescriptor) {
+ myValueDescriptor = valueDescriptor;
+ }
+
+ public void labelChanged() {
+ myTargetDescriptor.setValueLabel(constructLabelText(getDescriptorLabel(myKeyDescriptor), getDescriptorLabel(myValueDescriptor)));
+ myDelegate.labelChanged();
+ }
+
+ static String constructLabelText(final String keylabel, final String valueLabel) {
+ return keylabel + " -> " + valueLabel;
+ }
+
+ private static String getDescriptorLabel(final ValueDescriptorImpl keyDescriptor) {
+ return keyDescriptor == null? "null" : keyDescriptor.getValueLabel();
+ }
+ }
+
+ private static class ColorObjectRenderer extends CompoundReferenceRenderer {
+
+ public ColorObjectRenderer(final NodeRendererSettings rendererSettings) {
+ super(rendererSettings, "Color", null, null);
+ setClassName("java.awt.Color");
+ }
+
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ final ToStringRenderer toStringRenderer = myRendererSettings.getToStringRenderer();
+ if (toStringRenderer.isEnabled() && DebuggerManagerEx.getInstanceEx(evaluationContext.getProject()).getContext().isEvaluationPossible()) {
+ return toStringRenderer.calcLabel(descriptor, evaluationContext, listener);
+ }
+ return super.calcLabel(descriptor, evaluationContext, listener);
+ }
+
+ public Icon calcValueIcon(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ final Value value = descriptor.getValue();
+ if (value instanceof ObjectReference) {
+ try {
+ final ObjectReference objRef = (ObjectReference)value;
+ final ReferenceType refType = objRef.referenceType();
+ final Field valueField = refType.fieldByName("value");
+ if (valueField != null) {
+ final Value rgbValue = objRef.getValue(valueField);
+ if (rgbValue instanceof IntegerValue) {
+ final Color color = new Color(((IntegerValue)rgbValue).value(), true);
+ return new ColorIcon(16, 12, color, true);
+ }
+ }
+ }
+ catch (Exception e) {
+ throw new EvaluateException(e.getMessage(), e);
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java b/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java
new file mode 100644
index 0000000..a3eef6a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.util.containers.InternalIterator;
+import org.jdom.Element;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class RendererConfiguration implements Cloneable, JDOMExternalizable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.settings.NodeRendererSettings");
+
+ private static final int VERSION = 8;
+
+ private List<NodeRenderer> myRepresentationNodes = new ArrayList<NodeRenderer>();
+ private final NodeRendererSettings myRendererSettings;
+
+ protected RendererConfiguration(NodeRendererSettings rendererSettings) {
+ myRendererSettings = rendererSettings;
+ }
+
+ public RendererConfiguration clone() {
+ RendererConfiguration result = null;
+ try {
+ result = (RendererConfiguration)super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ LOG.error(e);
+ }
+ result.myRepresentationNodes = new ArrayList<NodeRenderer>();
+ for (Iterator<NodeRenderer> iterator = myRepresentationNodes.iterator(); iterator.hasNext();) {
+ result.addRenderer((NodeRenderer)iterator.next().clone());
+ }
+
+ return result;
+ }
+
+ public boolean equals(Object o) {
+ if(!(o instanceof RendererConfiguration)) return false;
+
+ return DebuggerUtilsEx.externalizableEqual(this, (RendererConfiguration)o);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(final Element element) throws WriteExternalException {
+ for (Iterator<NodeRenderer> iterator = myRepresentationNodes.iterator(); iterator.hasNext();) {
+ NodeRenderer renderer = iterator.next();
+ element.addContent(myRendererSettings.writeRenderer(renderer));
+ }
+ element.setAttribute("VERSION", String.valueOf(VERSION));
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void readExternal(final Element root) {
+ String versionAttrib = root.getAttributeValue("VERSION");
+ int configurationVersion = -1;
+ if (versionAttrib != null) {
+ try {
+ configurationVersion = Integer.parseInt(versionAttrib);
+ }
+ catch (NumberFormatException e) {
+ configurationVersion = -1;
+ }
+ }
+ if(configurationVersion != VERSION) {
+ return;
+ }
+
+ List<Element> children = root.getChildren(NodeRendererSettings.RENDERER_TAG);
+
+ myRepresentationNodes.clear();
+ for (Element nodeElement : children) {
+ try {
+ addRenderer((NodeRenderer)myRendererSettings.readRenderer(nodeElement));
+ }
+ catch (Exception e) {
+ LOG.debug(e);
+ }
+ }
+ }
+
+ public void addRenderer(NodeRenderer renderer) {
+ myRepresentationNodes.add(renderer);
+ }
+
+ public void removeRenderer(NodeRenderer renderer) {
+ myRepresentationNodes.remove(renderer);
+ }
+
+ public void removeAllRenderers() {
+ myRepresentationNodes.clear();
+ }
+
+ public void iterateRenderers(InternalIterator<NodeRenderer> iterator) {
+ for (Iterator<NodeRenderer> it = myRepresentationNodes.iterator(); it.hasNext();) {
+ final NodeRenderer renderer = it.next();
+ final boolean shouldContinue = iterator.visit(renderer);
+ if (!shouldContinue) {
+ break;
+ }
+ }
+ }
+
+ public int getRendererCount() {
+ return myRepresentationNodes.size();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/TextComponentBinding.java b/java/debugger/impl/src/com/intellij/debugger/settings/TextComponentBinding.java
new file mode 100644
index 0000000..773de1c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/TextComponentBinding.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.text.JTextComponent;
+import java.lang.reflect.Field;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 12, 2005
+ */
+public class TextComponentBinding extends FieldDataBinding{
+ private final JTextComponent myTextComponent;
+
+ public TextComponentBinding(@NonNls String dataFieldName, JTextComponent textComponent) {
+ super(dataFieldName);
+ myTextComponent = textComponent;
+ }
+
+ public void doLoadData(Object from, Field field) throws IllegalAccessException {
+ final String value = (String)field.get(from);
+ myTextComponent.setText(value);
+ }
+
+ public void doSaveData(Object to, Field field) throws IllegalAccessException{
+ field.set(to, myTextComponent.getText().trim());
+ }
+
+ protected boolean isModified(Object obj, Field field) throws IllegalAccessException {
+ final String value = (String)field.get(obj);
+ return myTextComponent.getText().trim().equals(value);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewConfigurable.form b/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewConfigurable.form
new file mode 100644
index 0000000..2eb90d9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewConfigurable.form
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.settings.ThreadsViewConfigurable">
+ <grid id="a2574" binding="myPanel" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="126" y="76" width="271" height="203"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="4cb6d" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="0" y="0" width="271" height="79"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1"/>
+ </constraints>
+ <properties/>
+ <border type="etched"/>
+ <children>
+ <component id="b8249" class="javax.swing.JCheckBox" binding="myShowGroupsCheckBox">
+ <constraints>
+ <xy x="2" y="2" width="140" height="27"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <margin top="5" left="2" bottom="2" right="2"/>
+ <text resource-bundle="messages/DebuggerBundle" key="label.threads.view.configurable.show.thread.groups"/>
+ </properties>
+ </component>
+ <component id="5bfe" class="javax.swing.JCheckBox" binding="myShowSyntheticsCheckBox">
+ <constraints>
+ <xy x="2" y="29" width="267" height="24"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.threads.view.configurable.show.stack.frames.for.synthetic.methods"/>
+ </properties>
+ </component>
+ <component id="a7f8b" class="javax.swing.JCheckBox" binding="myShowCurrentThreadChechBox">
+ <constraints>
+ <xy x="2" y="53" width="206" height="24"/>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.threads.view.configurable.current.thread.on.top"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="f4cba" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="0" y="84" width="271" height="79"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="1" fill="1"/>
+ </constraints>
+ <properties/>
+ <border type="etched"/>
+ <children>
+ <component id="4f5e9" class="javax.swing.JCheckBox" binding="myLineNumberCheckBox">
+ <constraints>
+ <xy x="2" y="2" width="128" height="27"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <margin top="5" left="2" bottom="2" right="2"/>
+ <text resource-bundle="messages/DebuggerBundle" key="label.threads.view.configurable.show.line.number"/>
+ </properties>
+ </component>
+ <component id="2fbc6" class="javax.swing.JCheckBox" binding="myClassNameCheckBox">
+ <constraints>
+ <xy x="2" y="29" width="123" height="24"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.threads.view.configurable.show.class.name"/>
+ </properties>
+ </component>
+ <component id="b56d7" class="javax.swing.JCheckBox" binding="mySourceCheckBox">
+ <constraints>
+ <xy x="2" y="53" width="156" height="24"/>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.threads.view.configurable.show.source.file.name"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewConfigurable.java
new file mode 100644
index 0000000..1015542
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewConfigurable.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.openapi.options.BaseConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+
+import javax.swing.*;
+import java.util.Iterator;
+
+/**
+ * @author Eugene Belyaev
+ */
+public class ThreadsViewConfigurable extends BaseConfigurable {
+ private final ThreadsViewSettings mySettings;
+ private JPanel myPanel;
+ private JCheckBox myShowGroupsCheckBox;
+ private JCheckBox myLineNumberCheckBox;
+ private JCheckBox myClassNameCheckBox;
+ private JCheckBox mySourceCheckBox;
+ private JCheckBox myShowSyntheticsCheckBox;
+ private JCheckBox myShowCurrentThreadChechBox;
+ private final CompositeDataBinding myDataBinding = new CompositeDataBinding();
+
+ public ThreadsViewConfigurable(ThreadsViewSettings settings) {
+ mySettings = settings;
+
+ myDataBinding.addBinding(new ToggleButtonBinding("SHOW_CLASS_NAME", myClassNameCheckBox));
+ myDataBinding.addBinding(new ToggleButtonBinding("SHOW_LINE_NUMBER", myLineNumberCheckBox));
+ myDataBinding.addBinding(new ToggleButtonBinding("SHOW_SOURCE_NAME", mySourceCheckBox));
+ myDataBinding.addBinding(new ToggleButtonBinding("SHOW_THREAD_GROUPS", myShowGroupsCheckBox));
+ myDataBinding.addBinding(new ToggleButtonBinding("SHOW_SYNTHETIC_FRAMES", myShowSyntheticsCheckBox));
+ myDataBinding.addBinding(new ToggleButtonBinding("SHOW_CURRENT_THREAD", myShowCurrentThreadChechBox));
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("threads.view.configurable.display.name");
+ }
+
+ public JComponent createComponent() {
+ return myPanel;
+ }
+
+ public void apply() {
+ myDataBinding.saveData(mySettings);
+ final Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
+ for (int i = 0; i < openProjects.length; i++) {
+ Project project = openProjects[i];
+ for (Iterator iterator = (DebuggerManagerEx.getInstanceEx(project)).getSessions().iterator(); iterator.hasNext();) {
+ ((DebuggerSession)iterator.next()).refresh(false);
+ }
+ }
+ }
+
+ public void reset() {
+ myDataBinding.loadData(mySettings);
+ }
+
+ public boolean isModified() {
+ return myDataBinding.isModified(mySettings);
+ }
+
+ public String getHelpTopic() {
+ return "reference.dialogs.customizeThreadView";
+ }
+
+ public void disposeUIResources() {
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewSettings.java
new file mode 100644
index 0000000..95fda53
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/ThreadsViewSettings.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.openapi.components.*;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+
+@State(
+ name="ThreadsViewSettings",
+ storages= {
+ @Storage(
+ file = StoragePathMacros.APP_CONFIG + "/debugger.threadsview.xml"
+ )}
+)
+public class ThreadsViewSettings implements PersistentStateComponent<ThreadsViewSettings> {
+ public boolean SHOW_THREAD_GROUPS = false;
+ public boolean SHOW_LINE_NUMBER = true;
+ public boolean SHOW_CLASS_NAME = true;
+ public boolean SHOW_SOURCE_NAME = false;
+ public boolean SHOW_SYNTHETIC_FRAMES = true;
+ public boolean SHOW_CURRENT_THREAD = true;
+
+ public static ThreadsViewSettings getInstance() {
+ return ServiceManager.getService(ThreadsViewSettings.class);
+ }
+
+ public ThreadsViewSettings getState() {
+ return this;
+ }
+
+ public void loadState(final ThreadsViewSettings state) {
+ XmlSerializerUtil.copyBean(state, this);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ToggleButtonBinding.java b/java/debugger/impl/src/com/intellij/debugger/settings/ToggleButtonBinding.java
new file mode 100644
index 0000000..03b850a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/ToggleButtonBinding.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.lang.reflect.Field;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 12, 2005
+ */
+public class ToggleButtonBinding extends FieldDataBinding{
+ private final JToggleButton myToggleButton;
+
+ public ToggleButtonBinding(@NonNls String dataFieldName, JToggleButton checkBox) {
+ super(dataFieldName);
+ myToggleButton = checkBox;
+ }
+
+ public void doLoadData(Object from, Field field) throws IllegalAccessException {
+ final Boolean value = (Boolean)field.get(from);
+ myToggleButton.setSelected(value.booleanValue());
+ }
+
+ public void doSaveData(Object to, Field field) throws IllegalAccessException{
+ field.set(to, myToggleButton.isSelected()? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ protected boolean isModified(Object obj, Field field) throws IllegalAccessException {
+ final Boolean value = (Boolean)field.get(obj);
+ return myToggleButton.isSelected() != value.booleanValue();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java
new file mode 100644
index 0000000..c52f403
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.ui.tree.render.CompoundNodeRenderer;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.util.IconUtil;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.containers.InternalIterator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 19, 2005
+ */
+public class UserRenderersConfigurable implements SearchableConfigurable, Configurable.NoScroll {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.settings.UserRenderersConfigurable");
+ private static final Icon ADD_ICON = IconUtil.getAddIcon();
+ private static final Icon REMOVE_ICON = IconUtil.getRemoveIcon();
+ private static final Icon COPY_ICON = PlatformIcons.COPY_ICON;
+ private static final Icon UP_ICON = IconUtil.getMoveUpIcon();
+ private static final Icon DOWN_ICON = IconUtil.getMoveDownIcon();
+
+ private JTextField myNameField;
+ private ElementsChooser<NodeRenderer> myRendererChooser;
+ private NodeRenderer myCurrentRenderer = null;
+ private final CompoundRendererConfigurable myRendererDataConfigurable;
+
+ public UserRenderersConfigurable(@Nullable Project project) {
+ myRendererDataConfigurable = new CompoundRendererConfigurable(project);
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("user.renderers.configurable.display.name");
+ }
+
+ public String getHelpTopic() {
+ return "reference.idesettings.debugger.typerenderers";
+ }
+
+ @NotNull
+ public String getId() {
+ return getHelpTopic();
+ }
+
+ public Runnable enableSearch(String option) {
+ return null;
+ }
+
+ public JComponent createComponent() {
+ final JPanel panel = new JPanel(new BorderLayout(4, 0));
+
+ final JComponent renderersList = createRenderersList();
+ final JComponent toolbar = createToolbar();
+ final JComponent rendererDataPanel = myRendererDataConfigurable.createComponent();
+
+ final JPanel left = new JPanel(new BorderLayout());
+
+ left.add(toolbar, BorderLayout.NORTH);
+ left.add(renderersList, BorderLayout.CENTER);
+
+ myNameField = new JTextField();
+ final JPanel nameFieldPanel = new JPanel(new BorderLayout());
+ nameFieldPanel.add(new JLabel(DebuggerBundle.message("label.user.renderers.configurable.renderer.name")), BorderLayout.WEST);
+ nameFieldPanel.add(myNameField, BorderLayout.CENTER);
+
+
+ final JPanel center = new JPanel(new BorderLayout(0, 4));
+
+ center.add(nameFieldPanel, BorderLayout.NORTH);
+ center.add(rendererDataPanel, BorderLayout.CENTER);
+
+ myNameField.getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(DocumentEvent e) {
+ if (myCurrentRenderer != null) {
+ myCurrentRenderer.setName(myNameField.getText());
+ myRendererChooser.refresh(myCurrentRenderer);
+ }
+ }
+ });
+
+ panel.add(left, BorderLayout.WEST);
+ panel.add(center, BorderLayout.CENTER);
+
+ return panel;
+ }
+
+ private JComponent createRenderersList() {
+ myRendererChooser = new ElementsChooser<NodeRenderer>(true);
+ myRendererChooser.getEmptyText().setText(DebuggerBundle.message("text.user.renderers.configurable.no.renderers"));
+
+ myRendererChooser.addElementsMarkListener(new ElementsChooser.ElementsMarkListener<NodeRenderer>() {
+ public void elementMarkChanged(final NodeRenderer element, final boolean isMarked) {
+ element.setEnabled(isMarked);
+ }
+ });
+ myRendererChooser.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) {
+ updateCurrentRenderer(myRendererChooser.getSelectedElements());
+ }
+ }
+ });
+ return myRendererChooser;
+ }
+
+ private void updateCurrentRenderer(List<NodeRenderer> selectedElements) {
+ if (selectedElements.size() != 1) {
+ // multiselection
+ setCurrentRenderer(null);
+ }
+ else {
+ setCurrentRenderer(selectedElements.get(0));
+ }
+ }
+
+ private void setCurrentRenderer(NodeRenderer renderer) {
+ if (myCurrentRenderer == renderer) {
+ return;
+ }
+ try {
+ if (myRendererDataConfigurable.isModified()) {
+ myRendererDataConfigurable.apply();
+ }
+ }
+ catch (ConfigurationException e) {
+ LOG.error(e);
+ }
+ myCurrentRenderer = renderer;
+ if (renderer != null) {
+ myNameField.setEnabled(true);
+ myNameField.setText(renderer.getName());
+ }
+ else {
+ myNameField.setEnabled(false);
+ myNameField.setText("");
+ }
+ myRendererDataConfigurable.setRenderer(renderer);
+ }
+
+ private JComponent createToolbar() {
+ final DefaultActionGroup group = new DefaultActionGroup();
+ group.add(new AddAction());
+ group.add(new RemoveAction());
+ group.add(new CopyAction());
+ group.add(new MoveAction(true));
+ group.add(new MoveAction(false));
+ final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
+ return toolbar.getComponent();
+ }
+
+ public void apply() throws ConfigurationException {
+ myRendererDataConfigurable.apply();
+ flushTo(NodeRendererSettings.getInstance().getCustomRenderers());
+ }
+
+ private void flushTo(final RendererConfiguration rendererConfiguration) {
+ rendererConfiguration.removeAllRenderers();
+ final int count = myRendererChooser.getElementCount();
+ for (int idx = 0; idx < count; idx++) {
+ rendererConfiguration.addRenderer(myRendererChooser.getElementAt(idx));
+ }
+ }
+
+ public boolean isModified() {
+ if (myRendererDataConfigurable.isModified()) {
+ return true;
+ }
+ final NodeRendererSettings settings = NodeRendererSettings.getInstance();
+ final RendererConfiguration rendererConfiguration = settings.getCustomRenderers();
+ if (myRendererChooser.getElementCount() != rendererConfiguration.getRendererCount()) {
+ return true;
+ }
+ final RendererConfiguration uiConfiguration = new RendererConfiguration(settings);
+ flushTo(uiConfiguration);
+ return !uiConfiguration.equals(rendererConfiguration);
+ }
+
+ public void reset() {
+ myRendererChooser.removeAllElements();
+ final RendererConfiguration rendererConfiguration = NodeRendererSettings.getInstance().getCustomRenderers();
+ final ArrayList<NodeRenderer> elementsToSelect = new ArrayList<NodeRenderer>(1);
+ rendererConfiguration.iterateRenderers(new InternalIterator<NodeRenderer>() {
+ public boolean visit(final NodeRenderer renderer) {
+ final NodeRenderer clonedRenderer = (NodeRenderer)renderer.clone();
+ myRendererChooser.addElement(clonedRenderer, clonedRenderer.isEnabled());
+ if (elementsToSelect.size() == 0) {
+ elementsToSelect.add(clonedRenderer);
+ }
+ return true;
+ }
+ });
+ myRendererChooser.selectElements(elementsToSelect);
+ updateCurrentRenderer(elementsToSelect);
+ myRendererDataConfigurable.reset();
+ }
+
+ public void disposeUIResources() {
+ myRendererChooser.removeAllElements();
+ myRendererDataConfigurable.disposeUIResources();
+ }
+
+ private class AddAction extends AnAction {
+ public AddAction() {
+ super(DebuggerBundle.message("button.add"), DebuggerBundle.message("user.renderers.configurable.button.description.add"), ADD_ICON);
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ final NodeRenderer renderer = (NodeRenderer)NodeRendererSettings.getInstance().createRenderer(CompoundNodeRenderer.UNIQUE_ID);
+ renderer.setEnabled(true);
+ myRendererChooser.addElement(renderer, renderer.isEnabled());
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ myNameField.requestFocus();
+ }
+ });
+ }
+ }
+
+ private class RemoveAction extends AnAction {
+ public RemoveAction() {
+ super(DebuggerBundle.message("button.remove"), DebuggerBundle.message("user.renderers.configurable.button.description.remove"), REMOVE_ICON);
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ for (NodeRenderer selectedElement : myRendererChooser.getSelectedElements()) {
+ myRendererChooser.removeElement(selectedElement);
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ presentation.setEnabled(myRendererChooser.getSelectedElement() != null);
+ }
+ }
+
+ private class CopyAction extends AnAction {
+ public CopyAction() {
+ super(DebuggerBundle.message("button.copy"), DebuggerBundle.message("user.renderers.configurable.button.description.copy"), COPY_ICON);
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ final NodeRenderer selectedElement = myRendererChooser.getSelectedElement();
+ if (selectedElement != null) {
+ final NodeRenderer cloned = (NodeRenderer)selectedElement.clone();
+ myRendererChooser.addElement(cloned, true);
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ presentation.setEnabled(myRendererChooser.getSelectedElement() != null);
+ }
+ }
+
+ private class MoveAction extends AnAction {
+ private final boolean myMoveUp;
+
+ public MoveAction(boolean up) {
+ super(up? DebuggerBundle.message("button.move.up") : DebuggerBundle.message("button.move.down"),
+ up? DebuggerBundle.message("user.renderers.configurable.button.description.move.up") : DebuggerBundle.message("user.renderers.configurable.button.description.move.down"),
+ up? UP_ICON : DOWN_ICON );
+ myMoveUp = up;
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ final int selectedRow = myRendererChooser.getSelectedElementRow();
+ if (selectedRow < 0) {
+ return;
+ }
+ int newRow = selectedRow + (myMoveUp? -1 : 1);
+ if (newRow < 0) {
+ newRow = myRendererChooser.getElementCount() - 1;
+ }
+ else if (newRow >= myRendererChooser.getElementCount()) {
+ newRow = 0;
+ }
+ myRendererChooser.moveElement(myRendererChooser.getElementAt(selectedRow), newRow);
+ }
+
+ public void update(AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ presentation.setEnabled(myRendererChooser.getSelectedElement() != null);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ViewsGeneralSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/ViewsGeneralSettings.java
new file mode 100644
index 0000000..8518e82
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/ViewsGeneralSettings.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.settings;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import org.jdom.Element;
+
+@State(
+ name="ViewsSettings",
+ storages= {
+ @Storage(
+ file = StoragePathMacros.APP_CONFIG + "/debugger.frameview.xml"
+ )}
+)
+public class ViewsGeneralSettings implements PersistentStateComponent<Element> {
+ public boolean SHOW_OBJECTID = true;
+ public boolean HIDE_NULL_ARRAY_ELEMENTS = true;
+ public boolean AUTOSCROLL_TO_NEW_LOCALS = true;
+ public boolean ENABLE_AUTO_EXPRESSIONS = true;
+
+ public ViewsGeneralSettings() {
+ }
+
+ public static ViewsGeneralSettings getInstance() {
+ return ServiceManager.getService(ViewsGeneralSettings.class);
+ }
+
+ public void loadState(Element element) {
+ try {
+ DefaultJDOMExternalizer.readExternal(this, element);
+ }
+ catch (InvalidDataException e) {
+ // ignore
+ }
+ }
+
+ public Element getState() {
+ Element element = new Element("ViewsGeneralSettings");
+ try {
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ }
+ catch (WriteExternalException e) {
+ // ignore
+ }
+ return element;
+ }
+
+ public boolean equals(Object object) {
+ if(!(object instanceof ViewsGeneralSettings)) return false;
+ ViewsGeneralSettings generalSettings = ((ViewsGeneralSettings) object);
+ return SHOW_OBJECTID == generalSettings.SHOW_OBJECTID &&
+ HIDE_NULL_ARRAY_ELEMENTS == generalSettings.HIDE_NULL_ARRAY_ELEMENTS &&
+ AUTOSCROLL_TO_NEW_LOCALS == generalSettings.AUTOSCROLL_TO_NEW_LOCALS;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/CompletedInputDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/CompletedInputDialog.java
new file mode 100644
index 0000000..979723a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/CompletedInputDialog.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.psi.PsiElement;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * User: lex
+ * Date: Sep 16, 2003
+ * Time: 6:43:46 PM
+ */
+public class CompletedInputDialog extends DialogWrapper {
+ JPanel myPanel;
+ DebuggerExpressionComboBox myCombo;
+ PsiElement myContext;
+ Project myProject;
+ private JLabel myLabel;
+
+ public CompletedInputDialog(String title, String okText, Project project) {
+ super(project, false);
+ setTitle(title);
+ setOKButtonText(okText);
+ myProject = project;
+ setModal(false);
+ DebuggerContextImpl debuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ myContext = PositionUtil.getContextElement(debuggerContext);
+ this.init();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myCombo.getPreferredFocusedComponent();
+ }
+
+ protected JComponent createCenterPanel() {
+ myPanel = new JPanel(new GridBagLayout());
+ myLabel = new JLabel(DebuggerBundle.message("label.complete.input.dialog.expression"));
+ myPanel.add(myLabel, new GridBagConstraints(0, 0, GridBagConstraints.REMAINDER, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+ new Insets(0, 1, 0, 1), 0, 0));
+
+ myCombo = new DebuggerExpressionComboBox(myProject, myContext, "evaluation", DefaultCodeFragmentFactory.getInstance());
+ myCombo.selectAll();
+
+ myPanel.add(myCombo, new GridBagConstraints(0, 1, GridBagConstraints.REMAINDER, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 1, 0, 1), 0, 0));
+ myPanel.setPreferredSize(new Dimension(200, 50));
+ return myPanel;
+ }
+
+ public TextWithImports getExpressionText() {
+ return myCombo.getText();
+ }
+
+ public DebuggerExpressionComboBox getCombo() {
+ return myCombo;
+ }
+
+ public void setExpressionLabel(String text) {
+ myLabel.setText(text);
+ }
+
+ public void dispose() {
+ myCombo.dispose();
+ super.dispose();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/CompletionEditor.java b/java/debugger/impl/src/com/intellij/debugger/ui/CompletionEditor.java
new file mode 100644
index 0000000..623d5c5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/CompletionEditor.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public abstract class CompletionEditor extends JComponent{
+ public abstract void setText (TextWithImports text);
+
+ public abstract TextWithImports getText();
+
+ public abstract void setContext(@Nullable PsiElement context);
+
+ public abstract PsiElement getContext();
+
+ public abstract void dispose();
+
+ public abstract String getRecentsId();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java
new file mode 100644
index 0000000..c60e811
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.CodeFragmentFactoryContextWrapper;
+import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.event.DocumentListener;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListPopup;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.ui.ClickListener;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author lex
+ */
+public abstract class DebuggerEditorImpl extends CompletionEditor{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.DebuggerEditorImpl");
+
+ public static final char SEPARATOR = 13;
+
+ private final Project myProject;
+ private PsiElement myContext;
+
+ private final String myRecentsId;
+
+ private final List<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
+ private Document myCurrentDocument;
+ private final JLabel myChooseFactory = new JLabel();
+ private WeakReference<ListPopup> myPopup;
+
+ private final PsiTreeChangeListener myPsiListener = new PsiTreeChangeAdapter() {
+ public void childRemoved(@NotNull PsiTreeChangeEvent event) {
+ checkContext();
+ }
+ public void childReplaced(@NotNull PsiTreeChangeEvent event) {
+ checkContext();
+ }
+ public void childMoved(@NotNull PsiTreeChangeEvent event) {
+ checkContext();
+ }
+ private void checkContext() {
+ final PsiElement contextElement = getContext();
+ if(contextElement == null || !contextElement.isValid()) {
+ final DebuggerManagerEx manager = DebuggerManagerEx.getInstanceEx(myProject);
+ if (manager == null) {
+ LOG.error("Cannot obtain debugger manager for project " + myProject);
+ }
+ final DebuggerContextImpl context = manager.getContextManager().getContext();
+ final PsiElement newContextElement = PositionUtil.getContextElement(context);
+ setContext(newContextElement != null && newContextElement.isValid()? newContextElement : null);
+ }
+ }
+ };
+ private CodeFragmentFactory myFactory;
+ protected boolean myInitialFactory;
+
+ public DebuggerEditorImpl(Project project, PsiElement context, String recentsId, final CodeFragmentFactory factory) {
+ myProject = project;
+ myContext = context;
+ myRecentsId = recentsId;
+ PsiManager.getInstance(project).addPsiTreeChangeListener(myPsiListener);
+ setFactory(factory);
+ myInitialFactory = true;
+
+ setFocusable(false);
+
+ myChooseFactory.setToolTipText("Click to change the language");
+ myChooseFactory.setBorder(new EmptyBorder(0, 3, 0, 3));
+ new ClickListener() {
+ @Override
+ public boolean onClick(MouseEvent e, int clickCount) {
+ ListPopup oldPopup = myPopup != null ? myPopup.get() : null;
+ if (oldPopup != null && !oldPopup.isDisposed()) {
+ oldPopup.cancel();
+ myPopup = null;
+ return true;
+ }
+
+ if (myContext == null) {
+ return true;
+ }
+
+ ListPopup popup = createLanguagePopup();
+ popup.showUnderneathOf(myChooseFactory);
+ myPopup = new WeakReference<ListPopup>(popup);
+ return true;
+ }
+ }.installOn(myChooseFactory);
+ }
+
+ private ListPopup createLanguagePopup() {
+ DefaultActionGroup actions = new DefaultActionGroup();
+ for (final CodeFragmentFactory fragmentFactory : DebuggerUtilsEx.getCodeFragmentFactories(myContext)) {
+ actions.add(new AnAction(fragmentFactory.getFileType().getLanguage().getDisplayName(), null, fragmentFactory.getFileType().getIcon()) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ setFactory(fragmentFactory);
+ setText(getText());
+ IdeFocusManager.getInstance(getProject()).requestFocus(DebuggerEditorImpl.this, true);
+ }
+ });
+ }
+
+ DataContext dataContext = DataManager.getInstance().getDataContext(this);
+ return JBPopupFactory.getInstance().createActionGroupPopup("Choose language", actions, dataContext,
+ JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
+ false);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ myChooseFactory.setEnabled(enabled);
+ super.setEnabled(enabled);
+ }
+
+ protected TextWithImports createItem(Document document, Project project) {
+ if (document != null) {
+ PsiDocumentManager.getInstance(project).commitDocument(document);
+ PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
+ if (psiFile != null) {
+ return createText(psiFile.getText(), ((JavaCodeFragment)psiFile).importsToString());
+ }
+ }
+
+ return createText("");
+ }
+
+ protected TextWithImports createText(String text) {
+ return createText(text, "");
+ }
+
+ protected abstract TextWithImports createText(String text, String importsString);
+
+ public abstract JComponent getPreferredFocusedComponent();
+
+ public void setContext(@Nullable PsiElement context) {
+ myContext = context;
+
+ List<CodeFragmentFactory> factories = DebuggerUtilsEx.getCodeFragmentFactories(context);
+ boolean many = factories.size() > 1;
+ if (myInitialFactory) {
+ myInitialFactory = false;
+ setFactory(factories.get(0));
+ myChooseFactory.setVisible(many);
+ }
+ myChooseFactory.setVisible(myChooseFactory.isVisible() || many);
+ myChooseFactory.setEnabled(many && factories.contains(myFactory));
+
+ updateEditorUi();
+ }
+
+ @Override
+ public void setText(TextWithImports text) {
+ doSetText(text);
+ updateEditorUi();
+ }
+
+ protected abstract void doSetText(TextWithImports text);
+
+ protected abstract void updateEditorUi();
+
+ public PsiElement getContext() {
+ return myContext;
+ }
+
+ protected Project getProject() {
+ return myProject;
+ }
+
+ public void requestFocus() {
+ getPreferredFocusedComponent().requestFocus();
+ }
+
+ @Nullable
+ protected Document createDocument(TextWithImports item) {
+ LOG.assertTrue(myContext == null || myContext.isValid());
+
+ if(item == null) {
+ item = createText("");
+ }
+ JavaCodeFragment codeFragment = getCurrentFactory().createPresentationCodeFragment(item, myContext, getProject());
+ codeFragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
+ if (myContext != null) {
+ final PsiClass contextClass = PsiTreeUtil.getNonStrictParentOfType(myContext, PsiClass.class);
+ if (contextClass != null) {
+ final PsiClassType contextType = JavaPsiFacade.getInstance(codeFragment.getProject()).getElementFactory().createType(contextClass);
+ codeFragment.setThisType(contextType);
+ }
+ }
+
+ if(myCurrentDocument != null) {
+ for (DocumentListener documentListener : myDocumentListeners) {
+ myCurrentDocument.removeDocumentListener(documentListener);
+ }
+ }
+
+ myCurrentDocument = PsiDocumentManager.getInstance(getProject()).getDocument(codeFragment);
+
+ if (myCurrentDocument != null) {
+ for (DocumentListener documentListener : myDocumentListeners) {
+ myCurrentDocument.addDocumentListener(documentListener);
+ }
+ }
+
+ return myCurrentDocument;
+ }
+
+ public String getRecentsId() {
+ return myRecentsId;
+ }
+
+ public void addRecent(TextWithImports text) {
+ if(getRecentsId() != null && text != null && !"".equals(text.getText())){
+ DebuggerRecents.getInstance(getProject()).addRecent(getRecentsId(), text);
+ }
+ }
+
+ public void addDocumentListener(DocumentListener listener) {
+ myDocumentListeners.add(listener);
+ if(myCurrentDocument != null) {
+ myCurrentDocument.addDocumentListener(listener);
+ }
+ }
+
+ public void removeDocumentListener(DocumentListener listener) {
+ myDocumentListeners.remove(listener);
+ if(myCurrentDocument != null) {
+ myCurrentDocument.removeDocumentListener(listener);
+ }
+ }
+
+ public void dispose() {
+ PsiManager.getInstance(myProject).removePsiTreeChangeListener(myPsiListener);
+ myCurrentDocument = null;
+ }
+
+ @NotNull
+ public static CodeFragmentFactory findAppropriateFactory(@NotNull TextWithImports text, @NotNull PsiElement context) {
+ for (CodeFragmentFactory factory : DebuggerUtilsEx.getCodeFragmentFactories(context)) {
+ if (factory.getFileType().equals(text.getFileType())) {
+ return factory;
+ }
+ }
+ return DefaultCodeFragmentFactory.getInstance();
+ }
+
+ protected void restoreFactory(TextWithImports text) {
+ FileType fileType = text.getFileType();
+ if (fileType == null) return;
+ if (myContext == null) return;
+
+ setFactory(findAppropriateFactory(text, myContext));
+ }
+
+ private void setFactory(@NotNull final CodeFragmentFactory factory) {
+ myFactory = factory;
+ Icon icon = getCurrentFactory().getFileType().getIcon();
+ myChooseFactory.setIcon(icon);
+ myChooseFactory.setDisabledIcon(IconLoader.getDisabledIcon(icon));
+ }
+
+ protected CodeFragmentFactory getCurrentFactory() {
+ return myFactory instanceof CodeFragmentFactoryContextWrapper ? myFactory : new CodeFragmentFactoryContextWrapper(myFactory);
+ }
+
+ protected JPanel addChooseFactoryLabel(JComponent component, boolean top) {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(component, BorderLayout.CENTER);
+
+ JPanel factoryPanel = new JPanel(new BorderLayout());
+ factoryPanel.add(myChooseFactory, top ? BorderLayout.NORTH : BorderLayout.CENTER);
+ panel.add(factoryPanel, BorderLayout.WEST);
+ return panel;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionComboBox.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionComboBox.java
new file mode 100644
index 0000000..9f9cf4a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionComboBox.java
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.EditorComboBoxEditor;
+import com.intellij.ui.EditorComboBoxRenderer;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.ComboPopup;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author ven
+ */
+public class DebuggerExpressionComboBox extends DebuggerEditorImpl {
+ public static final Key<String> KEY = Key.create("DebuggerComboBoxEditor.KEY");
+ public static final int MAX_ROWS = 20;
+
+ private MyEditorComboBoxEditor myEditor;
+ private ComboBox myComboBox;
+
+ private class MyEditorComboBoxEditor extends EditorComboBoxEditor {
+
+ public MyEditorComboBoxEditor(Project project, FileType fileType) {
+ super(project, fileType);
+ }
+
+ public Object getItem() {
+ Document document = (Document)super.getItem();
+ return createItem(document, getProject());
+ }
+
+ public void setItem(Object item) {
+ TextWithImports twi = (TextWithImports)item;
+ if (twi != null) {
+ restoreFactory(twi);
+ }
+ final Document document = createDocument(twi);
+ getEditorComponent().setNewDocumentAndFileType(getCurrentFactory().getFileType(), document);
+ super.setItem(document);
+ /* Causes PSI being modified from PSI events. See IDEADEV-22102
+ final Editor editor = getEditor();
+ if (editor != null) {
+ DaemonCodeAnalyzer.getInstance(getProject()).updateVisibleHighlighters(editor);
+ }
+ */
+ }
+
+ }
+
+ public DebuggerExpressionComboBox(Project project, @NonNls String recentsId) {
+ this(project, null, recentsId, DefaultCodeFragmentFactory.getInstance());
+ }
+
+ public DebuggerExpressionComboBox(Project project, PsiElement context, @NonNls String recentsId, final CodeFragmentFactory factory) {
+ super(project, context, recentsId, factory);
+ setLayout(new BorderLayout(0, 0));
+
+ myComboBox = new ComboBox(new MyComboboxModel(getRecents()), 100);
+ myComboBox.setSwingPopup(false);
+
+ // Have to turn this off because when used in DebuggerTreeInplaceEditor, the combobox popup is hidden on every change of selection
+ // See comment to SynthComboBoxUI.FocusHandler.focusLost()
+ myComboBox.setLightWeightPopupEnabled(false);
+
+ myEditor = new MyEditorComboBoxEditor(getProject(), getCurrentFactory().getFileType());
+ myComboBox.setRenderer(new EditorComboBoxRenderer(myEditor));
+
+ myComboBox.setEditable(true);
+ myComboBox.setEditor(myEditor);
+ add(addChooseFactoryLabel(myComboBox, false));
+ }
+
+ public void selectPopupValue() {
+ selectAll();
+ final Object currentPopupValue = getCurrentPopupValue();
+ if (currentPopupValue != null) {
+ myComboBox.getModel().setSelectedItem(currentPopupValue);
+ myComboBox.getEditor().setItem(currentPopupValue);
+ }
+
+ myComboBox.setPopupVisible(false);
+ }
+
+ public boolean isPopupVisible() {
+ return myComboBox.isVisible() && myComboBox.isPopupVisible();
+ }
+
+ public void setPopupVisible(final boolean b) {
+ myComboBox.setPopupVisible(b);
+ }
+
+ @Nullable
+ public Object getCurrentPopupValue() {
+ if (!isPopupVisible()) return null;
+
+ final ComboPopup popup = myComboBox.getPopup();
+ if (popup != null) {
+ return popup.getList().getSelectedValue();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void doSetText(TextWithImports item) {
+ final String itemText = item.getText().replace('\n', ' ');
+ restoreFactory(item);
+ item.setText(itemText);
+ if (!"".equals(itemText)) {
+ if (myComboBox.getItemCount() == 0 || !item.equals(myComboBox.getItemAt(0))) {
+ myComboBox.insertItemAt(item, 0);
+ }
+ }
+ if (myComboBox.getItemCount() > 0) {
+ myComboBox.setSelectedIndex(0);
+ }
+
+ myComboBox.getEditor().setItem(item);
+ }
+
+ @Override
+ protected void updateEditorUi() {
+ }
+
+ public TextWithImports getText() {
+ return (TextWithImports)myComboBox.getEditor().getItem();
+ }
+
+ @Nullable
+ private List<TextWithImports> getRecents() {
+ final String recentsId = getRecentsId();
+ if (recentsId != null) {
+ final List<TextWithImports> result = new ArrayList<TextWithImports>();
+ LinkedList<TextWithImports> recents = DebuggerRecents.getInstance(getProject()).getRecents(getRecentsId());
+ for (final TextWithImports evaluationText : recents) {
+ if (evaluationText.getText().indexOf('\n') == -1) {
+ result.add(evaluationText);
+ }
+ }
+
+ return result;
+ }
+
+ return null;
+ }
+
+ public Dimension getMinimumSize() {
+ Dimension size = super.getMinimumSize();
+ size.width = 100;
+ return size;
+ }
+
+ public TextWithImports createText(String text, String importsString) {
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, text, importsString, getCurrentFactory().getFileType());
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ myComboBox.setEnabled(enabled);
+ //if (enabled) {
+ // final ComboBoxEditor editor = myComboBox.getEditor();
+ // editor.setItem(editor.getItem());
+ //}
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return (JComponent)myComboBox.getEditor().getEditorComponent();
+ }
+
+ public void selectAll() {
+ myComboBox.getEditor().selectAll();
+ }
+
+ public Editor getEditor() {
+ return myEditor.getEditor();
+ }
+
+ public JComponent getEditorComponent() {
+ return (JComponent)myEditor.getEditorComponent();
+ }
+
+ public void addRecent(TextWithImports text) {
+ if (text.getText().length() != 0) {
+ final Component editorComponent = myComboBox.getEditor().getEditorComponent();
+ final boolean focusOwner = editorComponent.isFocusOwner();
+ super.addRecent(text);
+ myComboBox.insertItemAt(text, 0);
+ myComboBox.setSelectedIndex(0);
+
+ if (focusOwner) {
+ editorComponent.requestFocus();
+ }
+ }
+ }
+
+ private static class MyComboboxModel extends AbstractListModel implements MutableComboBoxModel {
+ private List<TextWithImports> myItems = new ArrayList<TextWithImports>();
+ private int mySelectedIndex = -1;
+
+ private MyComboboxModel(@Nullable final List<TextWithImports> recents) {
+ if (recents != null) {
+ myItems = recents;
+ }
+ }
+
+ @Override
+ public void setSelectedItem(final Object anItem) {
+ final int oldSelectedIndex = mySelectedIndex;
+ mySelectedIndex = anItem instanceof TextWithImports ? myItems.indexOf(anItem) : -1;
+ if (oldSelectedIndex != mySelectedIndex) fireContentsChanged(this, -1, -1);
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ return mySelectedIndex == -1 || mySelectedIndex > myItems.size() - 1 ? null : myItems.get(mySelectedIndex);
+ }
+
+ @Override
+ public int getSize() {
+ return myItems.size();
+ }
+
+ @Override
+ public Object getElementAt(int index) {
+ return myItems.get(index);
+ }
+
+ @Override
+ public void addElement(final Object obj) {
+ insertElementAt(obj, myItems.size() - 1);
+
+ if (mySelectedIndex == -1 && myItems.size() == 1 && obj != null) {
+ setSelectedItem(obj);
+ }
+ }
+
+ @Override
+ public void removeElement(Object obj) {
+ removeElement(obj, true);
+ }
+
+ public void removeElement(final Object obj, final boolean checkSelection) {
+ if (!(obj instanceof TextWithImports)) throw new IllegalArgumentException();
+ final int index = myItems.indexOf((TextWithImports)obj);
+ if (index != -1) {
+ myItems.remove(index);
+
+ if (checkSelection) {
+ if (mySelectedIndex == index) {
+ if (myItems.size() == 0) {
+ setSelectedItem(null);
+ }
+ else if (index > myItems.size() - 1) {
+ setSelectedItem(myItems.get(myItems.size() - 1));
+ }
+ }
+
+ fireIntervalRemoved(this, index, index);
+ }
+ }
+ }
+
+ @Override
+ public void insertElementAt(final Object obj, final int index) {
+ if (!(obj instanceof TextWithImports)) throw new IllegalArgumentException();
+ removeElement(obj, false); // remove duplicate entry if any
+
+ myItems.add(index, (TextWithImports)obj);
+ fireIntervalAdded(this, index, index);
+
+ if (myItems.size() > MAX_ROWS) {
+ for (int i = myItems.size() - 1; i > MAX_ROWS - 1; i--) {
+ myItems.remove(i);
+ }
+
+ // will not fire events here to not recreate the editor
+ //fireIntervalRemoved(this, myItems.size() - 1, MAX_ROWS - 1);
+ }
+ }
+
+ @Override
+ public void removeElementAt(final int index) {
+ if (index < 0 || index > myItems.size() - 1) throw new IndexOutOfBoundsException();
+ removeElement(myItems.get(index));
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionTextField.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionTextField.java
new file mode 100644
index 0000000..8dbdb3d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionTextField.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.EditorTextField;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DebuggerExpressionTextField extends DebuggerEditorImpl {
+ private final EditorTextField myEditor;
+ private final JTextField myStubField = new JTextField();
+ private final JPanel myMainPanel = new JPanel(new CardLayout());
+ private static final @NonNls String EDITOR = "editor";
+ private static final @NonNls String STUB = "stub";
+
+ public DebuggerExpressionTextField(Project project, PsiElement context, final @NonNls String recentsId) {
+ super(project, context, recentsId, DefaultCodeFragmentFactory.getInstance());
+ myStubField.setEnabled(false);
+ myEditor = new EditorTextField("", project, StdFileTypes.JAVA);
+ setLayout(new BorderLayout());
+ myMainPanel.add(myStubField, STUB);
+ myMainPanel.add(addChooseFactoryLabel(myEditor, false), EDITOR);
+ add(myMainPanel, BorderLayout.CENTER);
+ ((CardLayout)myMainPanel.getLayout()).show(myMainPanel, isEnabled()? EDITOR : STUB);
+ setText(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""));
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myEditor.getEditor().getContentComponent();
+ }
+
+ public void selectAll() {
+ myEditor.selectAll();
+ }
+
+ public TextWithImports getText() {
+ return createItem(myEditor.getDocument(), getProject());
+ }
+
+ @Override
+ protected void doSetText(TextWithImports text) {
+ restoreFactory(text);
+ myEditor.setNewDocumentAndFileType(getCurrentFactory().getFileType(), createDocument(text));
+ }
+
+ @Override
+ protected void updateEditorUi() {
+ final Editor editor = myEditor.getEditor();
+ if (editor != null) {
+ DaemonCodeAnalyzer.getInstance(getProject()).updateVisibleHighlighters(editor);
+ }
+ }
+
+ public TextWithImports createText(String text, String importsString) {
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, text, importsString, getCurrentFactory().getFileType());
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (isEnabled() != enabled) {
+ super.setEnabled(enabled);
+ final TextWithImports text = getText();
+ myStubField.setText(text.getText());
+ ((CardLayout)myMainPanel.getLayout()).show(myMainPanel, enabled? EDITOR : STUB);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerPanelsManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerPanelsManager.java
new file mode 100644
index 0000000..9cb61ef
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerPanelsManager.java
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.*;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextListener;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.MainWatchPanel;
+import com.intellij.debugger.ui.tree.render.BatchEvaluator;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionManager;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.executors.DefaultDebugExecutor;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.RunContentListener;
+import com.intellij.execution.ui.RunContentManager;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.colors.EditorColorsListener;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+
+public class DebuggerPanelsManager implements ProjectComponent {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.DebuggerPanelsManager");
+
+ private final Project myProject;
+ private final ExecutionManager myExecutionManager;
+
+ private final PositionHighlighter myEditorManager;
+ private final HashMap<ProcessHandler, DebuggerSessionTab> mySessionTabs = new HashMap<ProcessHandler, DebuggerSessionTab>();
+
+ public DebuggerPanelsManager(Project project, final EditorColorsManager colorsManager, ExecutionManager executionManager) {
+ myProject = project;
+ myExecutionManager = executionManager;
+
+ myEditorManager = new PositionHighlighter(myProject, getContextManager());
+
+ final EditorColorsListener myColorsListener = new EditorColorsListener() {
+ public void globalSchemeChange(EditorColorsScheme scheme) {
+ myEditorManager.updateContextPointDescription();
+ }
+ };
+ colorsManager.addEditorColorsListener(myColorsListener);
+ Disposer.register(project, new Disposable() {
+ public void dispose() {
+ colorsManager.removeEditorColorsListener(myColorsListener);
+ }
+ });
+
+ getContextManager().addListener(new DebuggerContextListener() {
+ public void changeEvent(final DebuggerContextImpl newContext, int event) {
+ if (event == DebuggerSession.EVENT_PAUSE) {
+ DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
+ public void run() {
+ toFront(newContext.getDebuggerSession());
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private DebuggerStateManager getContextManager() {
+ return DebuggerManagerEx.getInstanceEx(myProject).getContextManager();
+ }
+
+ @Nullable
+ public RunContentDescriptor attachVirtualMachine(Executor executor,
+ ProgramRunner runner,
+ ExecutionEnvironment environment,
+ RunProfileState state,
+ RunContentDescriptor reuseContent,
+ RemoteConnection remoteConnection,
+ boolean pollConnection) throws ExecutionException {
+ return attachVirtualMachine(new DefaultDebugUIEnvironment(myProject,
+ executor,
+ runner,
+ environment,
+ state,
+ reuseContent,
+ remoteConnection,
+ pollConnection));
+ }
+
+ @Nullable
+ public RunContentDescriptor attachVirtualMachine(DebugUIEnvironment environment) throws ExecutionException {
+ final DebugEnvironment modelEnvironment = environment.getEnvironment();
+ final DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(myProject).attachVirtualMachine(modelEnvironment);
+ if (debuggerSession == null) {
+ return null;
+ }
+
+ final DebugProcessImpl debugProcess = debuggerSession.getProcess();
+ if (debugProcess.isDetached() || debugProcess.isDetaching()) {
+ debuggerSession.dispose();
+ return null;
+ }
+ if (modelEnvironment.isRemote()) {
+ // optimization: that way BatchEvaluator will not try to lookup the class file in remote VM
+ // which is an expensive operation when executed first time
+ debugProcess.putUserData(BatchEvaluator.REMOTE_SESSION_KEY, Boolean.TRUE);
+ }
+
+ final DebuggerSessionTab sessionTab = new DebuggerSessionTab(myProject, modelEnvironment.getSessionName(), environment.getIcon());
+ Disposer.register(myProject, sessionTab);
+ RunContentDescriptor runContentDescriptor = sessionTab.attachToSession(debuggerSession, environment);
+ RunContentDescriptor reuseContent = environment.getReuseContent();
+ if (reuseContent != null) {
+ final ProcessHandler prevHandler = reuseContent.getProcessHandler();
+ if (prevHandler != null) {
+ final DebuggerSessionTab prevSession = mySessionTabs.get(prevHandler);
+ if (prevSession != null) {
+ sessionTab.reuse(prevSession);
+ }
+ }
+ }
+ mySessionTabs.put(runContentDescriptor.getProcessHandler(), sessionTab);
+ return runContentDescriptor;
+ }
+
+
+ public void projectOpened() {
+ final RunContentManager contentManager = myExecutionManager.getContentManager();
+ LOG.assertTrue(contentManager != null, "Content manager is null");
+
+ final RunContentListener myContentListener = new RunContentListener() {
+ public void contentSelected(RunContentDescriptor descriptor) {
+ DebuggerSessionTab sessionTab = descriptor != null ? getSessionTab(descriptor.getProcessHandler()) : null;
+
+ if (sessionTab != null) {
+ getContextManager()
+ .setState(sessionTab.getContextManager().getContext(), sessionTab.getSession().getState(), DebuggerSession.EVENT_CONTEXT, null);
+ }
+ else {
+ getContextManager()
+ .setState(DebuggerContextImpl.EMPTY_CONTEXT, DebuggerSession.STATE_DISPOSED, DebuggerSession.EVENT_CONTEXT, null);
+ }
+ }
+
+ public void contentRemoved(RunContentDescriptor descriptor) {
+ DebuggerSessionTab sessionTab = getSessionTab(descriptor.getProcessHandler());
+ if (sessionTab != null) {
+ mySessionTabs.remove(descriptor.getProcessHandler());
+ Disposer.dispose(sessionTab);
+ }
+ }
+ };
+
+ contentManager.addRunContentListener(myContentListener, DefaultDebugExecutor.getDebugExecutorInstance());
+ Disposer.register(myProject, new Disposable() {
+ public void dispose() {
+ contentManager.removeRunContentListener(myContentListener);
+ }
+ });
+ }
+
+ public void projectClosed() {
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "DebuggerPanelsManager";
+ }
+
+ public void initComponent() {
+ }
+
+ public void disposeComponent() {
+ }
+
+ public static DebuggerPanelsManager getInstance(Project project) {
+ return project.getComponent(DebuggerPanelsManager.class);
+ }
+
+ @Nullable
+ public MainWatchPanel getWatchPanel() {
+ DebuggerSessionTab sessionTab = getSessionTab();
+ return sessionTab != null ? sessionTab.getWatchPanel() : null;
+ }
+
+ @Nullable
+ public DebuggerSessionTab getSessionTab() {
+ DebuggerContextImpl context = DebuggerManagerEx.getInstanceEx(myProject).getContext();
+ return getSessionTab(context.getDebuggerSession());
+ }
+
+ public void showFramePanel() {
+ DebuggerSessionTab sessionTab = getSessionTab();
+ if (sessionTab != null) {
+ sessionTab.showFramePanel();
+ }
+ }
+
+ public void toFront(DebuggerSession session) {
+ DebuggerSessionTab sessionTab = getSessionTab(session);
+ if (sessionTab != null) {
+ sessionTab.toFront();
+ }
+ }
+
+ private DebuggerSessionTab getSessionTab(ProcessHandler processHandler) {
+ return mySessionTabs.get(processHandler);
+ }
+
+ @Nullable
+ private DebuggerSessionTab getSessionTab(DebuggerSession session) {
+ return session != null ? getSessionTab(session.getProcess().getExecutionResult().getProcessHandler()) : null;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerRecents.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerRecents.java
new file mode 100644
index 0000000..c314a0c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerRecents.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.containers.HashMap;
+
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * @author Lex
+ */
+public class DebuggerRecents {
+ private final Map<Object, LinkedList<TextWithImports>> myRecentExpressions = new HashMap<Object, LinkedList<TextWithImports>>();
+
+ public static DebuggerRecents getInstance(Project project) {
+ return ServiceManager.getService(project, DebuggerRecents.class);
+ }
+
+ public LinkedList<TextWithImports> getRecents(Object id) {
+ LinkedList<TextWithImports> result = myRecentExpressions.get(id);
+ if(result == null){
+ result = new LinkedList<TextWithImports>();
+ myRecentExpressions.put(id, result);
+ }
+ return result;
+ }
+
+ public void addRecent(Object id, TextWithImports recent) {
+ LinkedList<TextWithImports> recents = getRecents(id);
+ if(recents.size() >= DebuggerExpressionComboBox.MAX_ROWS) {
+ recents.removeLast();
+ }
+ recents.remove(recent);
+ recents.addFirst(recent);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java
new file mode 100644
index 0000000..9feb928
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java
@@ -0,0 +1,592 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebugUIEnvironment;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextListener;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.impl.MainWatchPanel;
+import com.intellij.debugger.ui.impl.ThreadsPanel;
+import com.intellij.debugger.ui.impl.VariablesPanel;
+import com.intellij.debugger.ui.impl.WatchDebuggerTree;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.execution.DefaultExecutionResult;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionManager;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.executors.DefaultDebugExecutor;
+import com.intellij.execution.filters.ExceptionFilters;
+import com.intellij.execution.filters.Filter;
+import com.intellij.execution.filters.TextConsoleBuilder;
+import com.intellij.execution.filters.TextConsoleBuilderFactory;
+import com.intellij.execution.ui.ConsoleView;
+import com.intellij.execution.ui.ExecutionConsoleEx;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.layout.PlaceInGrid;
+import com.intellij.icons.AllIcons;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.content.AlertIcon;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentManagerAdapter;
+import com.intellij.ui.content.ContentManagerEvent;
+import com.intellij.ui.content.tabs.PinToolwindowTabAction;
+import com.intellij.unscramble.ThreadDumpPanel;
+import com.intellij.unscramble.ThreadState;
+import com.intellij.xdebugger.XDebuggerBundle;
+import com.intellij.xdebugger.impl.actions.XDebuggerActions;
+import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase;
+import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+import java.util.List;
+
+public class DebuggerSessionTab extends DebuggerSessionTabBase implements Disposable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.DebuggerSessionTab");
+
+ private final VariablesPanel myVariablesPanel;
+ private final MainWatchPanel myWatchPanel;
+
+ private volatile DebuggerSession myDebuggerSession;
+
+ private final MyDebuggerStateManager myStateManager = new MyDebuggerStateManager();
+
+ private final FramesPanel myFramesPanel;
+ private DebugUIEnvironment myDebugUIEnvironment;
+
+ private final ThreadsPanel myThreadsPanel;
+ private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump";
+ private final Icon myIcon;
+
+ public DebuggerSessionTab(final Project project, final String sessionName, @Nullable final Icon icon) {
+ super(project, "JavaDebugger", sessionName);
+
+ myIcon = icon;
+
+ final DefaultActionGroup focus = new DefaultActionGroup();
+ focus.add(ActionManager.getInstance().getAction("Debugger.FocusOnBreakpoint"));
+ myUi.getOptions().setAdditionalFocusActions(focus);
+
+ final DebuggerSettings debuggerSettings = DebuggerSettings.getInstance();
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ getContextManager().addListener(new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ switch (event) {
+ case DebuggerSession.EVENT_DETACHED:
+ myUi.updateActionsNow();
+
+ if (debuggerSettings.HIDE_DEBUGGER_ON_PROCESS_TERMINATION) {
+ try {
+ ExecutionManager.getInstance(getProject()).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor);
+ }
+ catch (NullPointerException e) {
+ //if we can get closeProcess after the project have been closed
+ LOG.debug(e);
+ }
+ }
+ break;
+ }
+ }
+ });
+ }
+
+ DefaultActionGroup topToolbar = new DefaultActionGroup();
+ ActionManager actionManager = ActionManager.getInstance();
+ topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP));
+ topToolbar.add(actionManager.getAction(DebuggerActions.POP_FRAME), new Constraints(Anchor.AFTER, XDebuggerActions.STEP_OUT));
+ topToolbar.add(Separator.getInstance(), new Constraints(Anchor.BEFORE, DebuggerActions.POP_FRAME));
+ topToolbar.add(Separator.getInstance(), new Constraints(Anchor.AFTER, DebuggerActions.POP_FRAME));
+ myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
+
+
+ myWatchPanel = new MainWatchPanel(getProject(), getContextManager());
+ myFramesPanel = new FramesPanel(getProject(), getContextManager());
+
+
+ final AlertIcon breakpointAlert = new AlertIcon(AllIcons.Debugger.BreakpointAlert);
+
+ // watches
+ Content watches = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchPanel, XDebuggerBundle.message("debugger.session.tab.watches.title"),
+ AllIcons.Debugger.Watches, null);
+ watches.setCloseable(false);
+ watches.setAlertIcon(breakpointAlert);
+ myUi.addContent(watches, 0, PlaceInGrid.right, false);
+
+ // frames
+ Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, myFramesPanel, XDebuggerBundle.message("debugger.session.tab.frames.title"),
+ AllIcons.Debugger.Frame, myFramesPanel.getFramesList());
+ framesContent.setCloseable(false);
+ framesContent.setAlertIcon(breakpointAlert);
+
+ myUi.addContent(framesContent, 0, PlaceInGrid.left, false);
+
+ // variables
+ myVariablesPanel = new VariablesPanel(getProject(), myStateManager, this);
+ myVariablesPanel.getFrameTree().setAutoVariablesMode(debuggerSettings.AUTO_VARIABLES_MODE);
+ Content vars = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, myVariablesPanel, XDebuggerBundle.message("debugger.session.tab.variables.title"),
+ AllIcons.Debugger.Value, null);
+ vars.setCloseable(false);
+ vars.setAlertIcon(breakpointAlert);
+ myUi.addContent(vars, 0, PlaceInGrid.center, false);
+
+ // threads
+ myThreadsPanel = new ThreadsPanel(project, getContextManager());
+ Content threadsContent = myUi.createContent(DebuggerContentInfo.THREADS_CONTENT, myThreadsPanel, XDebuggerBundle.message("debugger.session.tab.threads.title"),
+ AllIcons.Debugger.Threads, null);
+ threadsContent.setCloseable(false);
+ //threadsContent.setAlertIcon(breakpointAlert);
+
+ //final DefaultActionGroup threadsGroup = new DefaultActionGroup();
+ //threadsContent.setActions(threadsGroup, ActionPlaces.DEBUGGER_TOOLBAR, threadsPanel.getThreadsTree());
+
+ myUi.addContent(threadsContent, 0, PlaceInGrid.left, true);
+
+ for (Content each : myUi.getContents()) {
+ updateStatus(each);
+ }
+
+ myUi.addListener(new ContentManagerAdapter() {
+ public void selectionChanged(ContentManagerEvent event) {
+ updateStatus(event.getContent());
+ }
+ }, this);
+ }
+
+ private static void updateStatus(final Content content) {
+ if (content.getComponent() instanceof DebuggerView) {
+ final DebuggerView view = (DebuggerView)content.getComponent();
+ if (content.isSelected()) {
+ view.setUpdateEnabled(true);
+ if (view.isRefreshNeeded()) {
+ view.rebuildIfVisible(DebuggerSession.EVENT_CONTEXT);
+ }
+ }
+ else {
+ view.setUpdateEnabled(false);
+ }
+ }
+ }
+
+ public MainWatchPanel getWatchPanel() {
+ return myWatchPanel;
+ }
+
+ @Override
+ public RunContentDescriptor getRunContentDescriptor() {
+ return myRunContentDescriptor;
+ }
+
+ private RunContentDescriptor initUI(ExecutionResult executionResult) {
+
+ myConsole = executionResult.getExecutionConsole();
+ myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName(), myIcon);
+
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return myRunContentDescriptor;
+ }
+
+
+ myUi.removeContent(myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT), true);
+
+ Content console = null;
+ if (myConsole instanceof ExecutionConsoleEx) {
+ ((ExecutionConsoleEx)myConsole).buildUi(myUi);
+ console = myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT);
+ if (console == null) {
+ LOG.debug("Reuse console created with non-debug runner");
+ }
+ }
+ if (console == null) {
+ console = myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(),
+ XDebuggerBundle.message("debugger.session.tab.console.content.name"),
+ AllIcons.Debugger.Console, myConsole.getPreferredFocusableComponent());
+
+ console.setCloseable(false);
+ myUi.addContent(console, 1, PlaceInGrid.bottom, false);
+ }
+ attachNotificationTo(console);
+
+ if (myConsole != null) {
+ Disposer.register(this, myConsole);
+ }
+
+ final DefaultActionGroup consoleActions = new DefaultActionGroup();
+ if (myConsole instanceof ConsoleView) {
+ AnAction[] actions = ((ConsoleView)myConsole).createConsoleActions();
+ for (AnAction goaction : actions) {
+ consoleActions.add(goaction);
+ }
+ }
+ console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR, myConsole.getPreferredFocusableComponent());
+
+ myDebugUIEnvironment.initLogs(myRunContentDescriptor, getLogManager());
+
+ DefaultActionGroup leftToolbar = new DefaultActionGroup();
+
+ if (executionResult instanceof DefaultExecutionResult) {
+ final AnAction[] actions = ((DefaultExecutionResult)executionResult).getRestartActions();
+ if (actions != null) {
+ leftToolbar.addAll(actions);
+ if (actions.length > 0) {
+ leftToolbar.addSeparator();
+ }
+ }
+ }
+ final AnAction[] profileActions = executionResult.getActions();
+ leftToolbar.addAll(profileActions);
+
+ leftToolbar.add(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP));
+ if (executionResult instanceof DefaultExecutionResult) {
+ AnAction[] actions = ((DefaultExecutionResult)executionResult).getAdditionalStopActions();
+ for (AnAction action : actions) {
+ leftToolbar.add(action, new Constraints(Anchor.AFTER, IdeActions.ACTION_STOP_PROGRAM));
+ }
+ }
+
+ leftToolbar.addSeparator();
+ addAction(leftToolbar, DebuggerActions.EXPORT_THREADS);
+ addAction(leftToolbar, DebuggerActions.DUMP_THREADS);
+ leftToolbar.addSeparator();
+
+ leftToolbar.add(myUi.getOptions().getLayoutActions());
+
+ final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList();
+ final AnAction commonSettingsList = myUi.getOptions().getSettingsActions();
+
+ final DefaultActionGroup settings = new DefaultActionGroup("DebuggerSettings", true) {
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setText(ActionsBundle.message("group.XDebugger.settings.text"));
+ e.getPresentation().setIcon(commonSettingsList.getTemplatePresentation().getIcon());
+ }
+
+ @Override
+ public boolean isDumbAware() {
+ return true;
+ }
+ };
+ for (AnAction each : commonSettings) {
+ settings.add(each);
+ }
+ if (commonSettings.length > 0) {
+ settings.addSeparator();
+ }
+ settings.add(new WatchLastMethodReturnValueAction());
+ settings.add(new AutoVarsSwitchAction());
+ settings.addSeparator();
+ addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP);
+
+ leftToolbar.add(settings);
+
+ leftToolbar.addSeparator();
+
+ addActionToGroup(leftToolbar, PinToolwindowTabAction.ACTION_NAME);
+
+ myDebugUIEnvironment.initActions(myRunContentDescriptor, leftToolbar);
+
+ myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
+
+ return myRunContentDescriptor;
+ }
+
+ private static void addAction(DefaultActionGroup group, String actionId) {
+ group.add(ActionManager.getInstance().getAction(actionId));
+ }
+
+ private static void addActionToGroup(final DefaultActionGroup group, final String actionId) {
+ AnAction action = ActionManager.getInstance().getAction(actionId);
+ if (action != null) group.add(action);
+ }
+
+
+ public void dispose() {
+ disposeSession();
+ myFramesPanel.dispose();
+ myVariablesPanel.dispose();
+ myWatchPanel.dispose();
+ myThreadsPanel.dispose();
+ myConsole = null;
+ super.dispose();
+ }
+
+ private void disposeSession() {
+ final DebuggerSession session = myDebuggerSession;
+ myDebuggerSession = null;
+ if (session != null) {
+ session.dispose();
+ }
+ }
+
+ @Nullable
+ private DebugProcessImpl getDebugProcess() {
+ final DebuggerSession session = myDebuggerSession;
+ return session != null ? session.getProcess() : null;
+ }
+
+ public void reuse(DebuggerSessionTab reuseSession) {
+ DebuggerTreeNodeImpl[] watches = reuseSession.getWatchPanel().getWatchTree().getWatches();
+
+ final WatchDebuggerTree watchTree = getWatchPanel().getWatchTree();
+ for (DebuggerTreeNodeImpl watch : watches) {
+ watchTree.addWatch((WatchItemDescriptor)watch.getDescriptor());
+ }
+ }
+
+ public String getSessionName() {
+ return myDebugUIEnvironment.getEnvironment().getSessionName();
+ }
+
+ public DebuggerStateManager getContextManager() {
+ return myStateManager;
+ }
+
+ @Nullable
+ public TextWithImports getSelectedExpression() {
+ final DebuggerSession session = myDebuggerSession;
+ if (session == null || session.getState() != DebuggerSession.STATE_PAUSED) {
+ return null;
+ }
+ JTree tree = myVariablesPanel.getFrameTree();
+ if (tree == null || !tree.hasFocus()) {
+ tree = myWatchPanel.getWatchTree();
+ if (tree == null || !tree.hasFocus()) {
+ return null;
+ }
+ }
+ TreePath path = tree.getSelectionPath();
+ if (path == null) {
+ return null;
+ }
+ DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)path.getLastPathComponent();
+ if (node == null) {
+ return null;
+ }
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (!(descriptor instanceof ValueDescriptorImpl)) {
+ return null;
+ }
+ if (descriptor instanceof WatchItemDescriptor) {
+ return ((WatchItemDescriptor)descriptor).getEvaluationText();
+ }
+ try {
+ return DebuggerTreeNodeExpression.createEvaluationText(node, getContextManager().getContext());
+ }
+ catch (EvaluateException e) {
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ protected RunProfile getRunProfile() {
+ return myDebugUIEnvironment != null ? myDebugUIEnvironment.getRunProfile() : null;
+ }
+
+ public RunContentDescriptor attachToSession(final DebuggerSession session, DebugUIEnvironment environment) throws ExecutionException {
+ disposeSession();
+ myDebuggerSession = session;
+ myDebugUIEnvironment = environment;
+
+ session.getContextManager().addListener(new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ if (!myUi.isDisposed()) {
+ attractFramesOnPause(event);
+ myStateManager.fireStateChanged(newContext, event);
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (!myUi.isDisposed()) {
+ myUi.updateActionsNow();
+ }
+ }
+ });
+ }
+ }
+ });
+ return initUI(session.getProcess().getExecutionResult());
+ }
+
+ private void attractFramesOnPause(final int event) {
+ if (DebuggerSession.EVENT_PAUSE == event) {
+ myUi.attractBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
+ }
+ else if (DebuggerSession.EVENT_RESUME == event) {
+ myUi.clearAttractionBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
+ }
+ }
+
+ public DebuggerSession getSession() {
+ return myDebuggerSession;
+ }
+
+ public void showFramePanel() {
+ myUi.selectAndFocus(myUi.findContent(DebuggerContentInfo.FRAME_CONTENT), true, false);
+ }
+
+ private int myThreadDumpsCount = 0;
+ private int myCurrentThreadDumpId = 1;
+
+ public void addThreadDump(List<ThreadState> threads) {
+ final Project project = getProject();
+ final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
+ List<Filter> filters = ExceptionFilters.getFilters(myDebuggerSession.getSearchScope());
+ for (Filter filter : filters) {
+ consoleBuilder.addFilter(filter);
+ }
+ final ConsoleView consoleView = consoleBuilder.getConsole();
+ final DefaultActionGroup toolbarActions = new DefaultActionGroup();
+ consoleView.allowHeavyFilters();
+ final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads);
+
+ final Icon icon = null;
+ final String id = createThreadDumpContentId();
+ final Content content = myUi.createContent(id, panel, id, icon, null);
+ content.setCloseable(true);
+ content.setDescription("Thread Dump");
+ myUi.addContent(content);
+ myUi.selectAndFocus(content, true, true);
+ myThreadDumpsCount += 1;
+ myCurrentThreadDumpId += 1;
+ Disposer.register(this, new Disposable() {
+ public void dispose() {
+ myUi.removeContent(content, true);
+ }
+ });
+ Disposer.register(content, new Disposable() {
+ public void dispose() {
+ myThreadDumpsCount -= 1;
+ if (myThreadDumpsCount == 0) {
+ myCurrentThreadDumpId = 1;
+ }
+ }
+ });
+ myUi.selectAndFocus(content, true, false);
+ if (threads.size() > 0) {
+ panel.selectStackFrame(0);
+ }
+ }
+
+ private String createThreadDumpContentId() {
+ return THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId;
+ }
+
+ private class MyDebuggerStateManager extends DebuggerStateManager {
+ public void fireStateChanged(DebuggerContextImpl newContext, int event) {
+ super.fireStateChanged(newContext, event);
+ }
+
+ public DebuggerContextImpl getContext() {
+ final DebuggerSession session = myDebuggerSession;
+ return session != null ? session.getContextManager().getContext() : DebuggerContextImpl.EMPTY_CONTEXT;
+ }
+
+ public void setState(DebuggerContextImpl context, int state, int event, String description) {
+ final DebuggerSession session = myDebuggerSession;
+ if (session != null) {
+ session.getContextManager().setState(context, state, event, description);
+ }
+ }
+ }
+
+ private class AutoVarsSwitchAction extends ToggleAction {
+ private volatile boolean myAutoModeEnabled;
+ private static final String myAutoModeText = "Auto-Variables Mode";
+ private static final String myDefaultModeText = "All-Variables Mode";
+
+ public AutoVarsSwitchAction() {
+ super("", "", AllIcons.Debugger.AutoVariablesMode);
+ myAutoModeEnabled = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE;
+ }
+
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ final boolean autoModeEnabled = (Boolean)presentation.getClientProperty(SELECTED_PROPERTY);
+ presentation.setText(autoModeEnabled ? myDefaultModeText : myAutoModeText);
+ }
+
+ public boolean isSelected(AnActionEvent e) {
+ return myAutoModeEnabled;
+ }
+
+ public void setSelected(AnActionEvent e, boolean enabled) {
+ myAutoModeEnabled = enabled;
+ DebuggerSettings.getInstance().AUTO_VARIABLES_MODE = enabled;
+ myVariablesPanel.getFrameTree().setAutoVariablesMode(enabled);
+ }
+ }
+
+ private class WatchLastMethodReturnValueAction extends ToggleAction {
+ private volatile boolean myWatchesReturnValues;
+ private final String myTextEnable;
+ private final String myTextUnavailable;
+ private final String myMyTextDisable;
+
+ public WatchLastMethodReturnValueAction() {
+ super("", DebuggerBundle.message("action.watch.method.return.value.description"), null);
+ myWatchesReturnValues = DebuggerSettings.getInstance().WATCH_RETURN_VALUES;
+ myTextEnable = DebuggerBundle.message("action.watches.method.return.value.enable");
+ myMyTextDisable = DebuggerBundle.message("action.watches.method.return.value.disable");
+ myTextUnavailable = DebuggerBundle.message("action.watches.method.return.value.unavailable.reason");
+ }
+
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ final boolean watchValues = (Boolean)presentation.getClientProperty(SELECTED_PROPERTY);
+ final DebugProcessImpl process = getDebugProcess();
+ final String actionText = watchValues ? myMyTextDisable : myTextEnable;
+ if (process != null && process.canGetMethodReturnValue()) {
+ presentation.setEnabled(true);
+ presentation.setText(actionText);
+ }
+ else {
+ presentation.setEnabled(false);
+ presentation.setText(process == null ? actionText : myTextUnavailable);
+ }
+ }
+
+ public boolean isSelected(AnActionEvent e) {
+ return myWatchesReturnValues;
+ }
+
+ public void setSelected(AnActionEvent e, boolean watch) {
+ myWatchesReturnValues = watch;
+ DebuggerSettings.getInstance().WATCH_RETURN_VALUES = watch;
+ final DebugProcessImpl process = getDebugProcess();
+ if (process != null) {
+ process.setWatchMethodReturnValuesEnabled(watch);
+ }
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerStatementEditor.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerStatementEditor.java
new file mode 100644
index 0000000..fb378f7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerStatementEditor.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
+import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.EditorTextField;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * @author lex
+ */
+public class DebuggerStatementEditor extends DebuggerEditorImpl {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.DebuggerStatementEditor");
+
+ private final EditorTextField myEditor;
+
+ private int myRecentIdx;
+
+ public DebuggerStatementEditor(Project project, PsiElement context, @NonNls String recentsId, final CodeFragmentFactory factory) {
+ super(project, context, recentsId, factory);
+ myRecentIdx = getRecentItemsCount();
+ final Document document = EditorFactory.getInstance().createDocument("");
+ myEditor = new EditorTextField(document, project, factory.getFileType(), false, false) {
+ protected EditorEx createEditor() {
+ EditorEx editor = super.createEditor();
+ editor.setVerticalScrollbarVisible(true);
+ editor.setHorizontalScrollbarVisible(true);
+ return editor;
+ }
+ };
+
+ setLayout(new BorderLayout());
+ add(addChooseFactoryLabel(myEditor, true));
+
+ DefaultActionGroup actionGroup = new DefaultActionGroup(null, false);
+ actionGroup.add(new ItemAction(IdeActions.ACTION_PREVIOUS_OCCURENCE, this){
+ public void actionPerformed(AnActionEvent e) {
+ LOG.assertTrue(myRecentIdx >= 0);
+ // since recents are stored in a stack, previous item is at currentIndex + 1
+ myRecentIdx += 1;
+ updateTextFromRecents();
+ }
+
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(myRecentIdx < getRecentItemsCount());
+ }
+ });
+ actionGroup.add(new ItemAction(IdeActions.ACTION_NEXT_OCCURENCE, this){
+ public void actionPerformed(AnActionEvent e) {
+ if(LOG.isDebugEnabled()) {
+ LOG.assertTrue(myRecentIdx < getRecentItemsCount());
+ }
+ // since recents are stored in a stack, next item is at currentIndex - 1
+ myRecentIdx -= 1;
+ updateTextFromRecents();
+ }
+
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(myRecentIdx > 0);
+ }
+ });
+
+ add(ActionManager.getInstance().createActionToolbar(ActionPlaces.COMBO_PAGER, actionGroup, false).getComponent(),
+ BorderLayout.EAST);
+
+ setText(new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, ""));
+ }
+
+ private void updateTextFromRecents() {
+ List<TextWithImports> recents = getRecents();
+ LOG.assertTrue(myRecentIdx <= recents.size());
+ setText(myRecentIdx < recents.size() ? recents.get(myRecentIdx) : new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""));
+ }
+
+ private List<TextWithImports> getRecents() {
+ final LinkedList<TextWithImports> recents = DebuggerRecents.getInstance(getProject()).getRecents(getRecentsId());
+ final ArrayList<TextWithImports> reversed = new ArrayList<TextWithImports>(recents.size());
+ for (final ListIterator<TextWithImports> it = recents.listIterator(recents.size()); it.hasPrevious();) {
+ reversed.add(it.previous());
+ }
+ return reversed;
+ }
+
+ private int getRecentItemsCount() {
+ return DebuggerRecents.getInstance(getProject()).getRecents(getRecentsId()).size();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ final Editor editor = myEditor.getEditor();
+ return editor != null? editor.getContentComponent() : myEditor;
+ }
+
+ public TextWithImports getText() {
+ return createItem(myEditor.getDocument(), getProject());
+ }
+
+ @Override
+ protected void doSetText(TextWithImports text) {
+ restoreFactory(text);
+ myEditor.setNewDocumentAndFileType(getCurrentFactory().getFileType(), createDocument(text));
+ }
+
+ @Override
+ protected void updateEditorUi() {
+ final Editor editor = myEditor.getEditor();
+ if (editor != null) {
+ DaemonCodeAnalyzer.getInstance(getProject()).updateVisibleHighlighters(editor);
+ }
+ }
+
+ public TextWithImports createText(String text, String importsString) {
+ return new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, text, importsString, getCurrentFactory().getFileType());
+ }
+
+ private static abstract class ItemAction extends AnAction {
+ public ItemAction(String sourceActionName, JComponent component) {
+ copyFrom(ActionManager.getInstance().getAction(sourceActionName));
+ registerCustomShortcutSet(getShortcutSet(), component);
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerView.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerView.java
new file mode 100644
index 0000000..048980d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerView.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+public interface DebuggerView {
+
+ void setUpdateEnabled(boolean enabled);
+
+ boolean isRefreshNeeded();
+ void rebuildIfVisible(final int eventContext);
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java b/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java
new file mode 100644
index 0000000..414bdd3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.codeInsight.hint.HintManager;
+import com.intellij.codeInsight.hint.HintManagerImpl;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author lex
+ */
+public abstract class EditorEvaluationCommand<T> extends DebuggerContextCommandImpl {
+ protected final PsiElement myElement;
+ @Nullable private final Editor myEditor;
+ protected final ProgressIndicator myProgressIndicator;
+ private final DebuggerContextImpl myDebuggerContext;
+
+ public EditorEvaluationCommand(@Nullable Editor editor, PsiElement expression, DebuggerContextImpl context,
+ final ProgressIndicator indicator) {
+ super(context);
+ Project project = expression.getProject();
+ myProgressIndicator = indicator;
+ myEditor = editor;
+ myElement = expression;
+ myDebuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ }
+
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+
+ protected abstract T evaluate(EvaluationContextImpl evaluationContext) throws EvaluateException;
+
+ public T evaluate() throws EvaluateException {
+ myProgressIndicator.setText(DebuggerBundle.message("progress.evaluating", ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ public String compute() {
+ return myElement.getText();
+ }
+ })));
+
+ try {
+ T result = evaluate(myDebuggerContext.createEvaluationContext());
+
+ if (myProgressIndicator.isCanceled()) throw new ProcessCanceledException();
+
+ return result;
+ } catch (final EvaluateException e) {
+ if (myEditor != null) {
+ DebuggerInvocationUtil.invokeLater(myDebuggerContext.getProject(), new Runnable() {
+ public void run() {
+ showEvaluationHint(myEditor, myElement, e);
+ }
+ }, myProgressIndicator.getModalityState());
+ }
+ throw e;
+ }
+ }
+
+ public static void showEvaluationHint(final Editor myEditor, final PsiElement myElement, final EvaluateException e) {
+ if (myEditor.isDisposed() || !myEditor.getComponent().isVisible()) return;
+
+ HintManager.getInstance().showErrorHint(myEditor, e.getMessage(), myElement.getTextRange().getStartOffset(),
+ myElement.getTextRange().getEndOffset(), HintManagerImpl.UNDER,
+ HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.HIDE_BY_TEXT_CHANGE,
+ 1500);
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java
new file mode 100644
index 0000000..4730f94
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.CommonBundle;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextListener;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.ui.impl.WatchDebuggerTree;
+import com.intellij.debugger.ui.impl.WatchPanel;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.EvaluationDescriptor;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.psi.PsiElement;
+import com.intellij.xdebugger.XDebuggerBundle;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.TreeModel;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class EvaluationDialog extends DialogWrapper {
+ private final MyEvaluationPanel myEvaluationPanel;
+ private final Project myProject;
+ private final DebuggerContextListener myContextListener;
+ private final DebuggerEditorImpl myEditor;
+ private final List<Runnable> myDisposeRunnables = new ArrayList<Runnable>();
+
+ public EvaluationDialog(Project project, TextWithImports text) {
+ super(project, true);
+ myProject = project;
+ setModal(false);
+ setCancelButtonText(CommonBundle.message("button.close"));
+ setOKButtonText(DebuggerBundle.message("button.evaluate"));
+
+ myEvaluationPanel = new MyEvaluationPanel(myProject);
+
+ myEditor = createEditor(DefaultCodeFragmentFactory.getInstance());
+
+ setDebuggerContext(getDebuggerContext());
+ initDialogData(text);
+
+ myContextListener = new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ boolean close = true;
+ for (DebuggerSession session : DebuggerManagerEx.getInstanceEx(myProject).getSessions()) {
+ if (!session.isStopped()) {
+ close = false;
+ break;
+ }
+ }
+
+ if(close) {
+ close(CANCEL_EXIT_CODE);
+ }
+ else {
+ setDebuggerContext(newContext);
+ }
+ }
+ };
+ DebuggerManagerEx.getInstanceEx(myProject).getContextManager().addListener(myContextListener);
+
+ setHorizontalStretch(1f);
+ setVerticalStretch(1f);
+ }
+
+ protected void doOKAction() {
+ if (isOKActionEnabled()) {
+ doEvaluate();
+ }
+ }
+
+ protected void doEvaluate() {
+ if (myEditor == null || myEvaluationPanel == null) {
+ return;
+ }
+
+ myEvaluationPanel.clear();
+ TextWithImports codeToEvaluate = getCodeToEvaluate();
+ if (codeToEvaluate == null) {
+ return;
+ }
+ try {
+ setOKActionEnabled(false);
+ NodeDescriptorImpl descriptor = myEvaluationPanel.getWatchTree().addWatch(codeToEvaluate, "result").getDescriptor();
+ if (descriptor instanceof EvaluationDescriptor) {
+ final EvaluationDescriptor evalDescriptor = (EvaluationDescriptor)descriptor;
+ evalDescriptor.setCodeFragmentFactory(myEditor.getCurrentFactory());
+ }
+ myEvaluationPanel.getWatchTree().rebuild(getDebuggerContext());
+ descriptor.myIsExpanded = true;
+ }
+ finally {
+ setOKActionEnabled(true);
+ }
+ getEditor().addRecent(getCodeToEvaluate());
+
+ myEvaluationPanel.getContextManager().getContext().getDebuggerSession().refresh(true);
+ }
+
+ @Nullable
+ protected TextWithImports getCodeToEvaluate() {
+ TextWithImports text = getEditor().getText();
+ String s = text.getText();
+ if (s != null) {
+ s = s.trim();
+ }
+ if ("".equals(s)) {
+ return null;
+ }
+ return text;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myEditor.getPreferredFocusedComponent();
+ }
+
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.debugger.ui.EvaluationDialog2";
+ }
+
+ protected void addDisposeRunnable (Runnable runnable) {
+ myDisposeRunnables.add(runnable);
+ }
+
+ public void dispose() {
+ for (Runnable runnable : myDisposeRunnables) {
+ runnable.run();
+ }
+ myDisposeRunnables.clear();
+ myEditor.dispose();
+ DebuggerManagerEx.getInstanceEx(myProject).getContextManager().removeListener(myContextListener);
+ myEvaluationPanel.dispose();
+ super.dispose();
+ }
+
+ protected class MyEvaluationPanel extends WatchPanel {
+ public MyEvaluationPanel(final Project project) {
+ super(project, (DebuggerManagerEx.getInstanceEx(project)).getContextManager());
+ final WatchDebuggerTree watchTree = getWatchTree();
+ final AnAction setValueAction = ActionManager.getInstance().getAction(DebuggerActions.SET_VALUE);
+ setValueAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)), watchTree);
+ registerDisposable(new Disposable() {
+ public void dispose() {
+ setValueAction.unregisterCustomShortcutSet(watchTree);
+ }
+ });
+ setUpdateEnabled(true);
+ getTree().getEmptyText().setText(XDebuggerBundle.message("debugger.no.results"));
+ }
+
+ protected ActionPopupMenu createPopupMenu() {
+ ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.EVALUATION_DIALOG_POPUP);
+ return ActionManager.getInstance().createActionPopupMenu(DebuggerActions.EVALUATION_DIALOG_POPUP, group);
+ }
+
+ protected void changeEvent(DebuggerContextImpl newContext, int event) {
+ if (event == DebuggerSession.EVENT_REFRESH || event == DebuggerSession.EVENT_REFRESH_VIEWS_ONLY) {
+ // in order not to spoil the evaluation result do not re-evaluate the tree
+ final TreeModel treeModel = getTree().getModel();
+ updateTree(treeModel, (DebuggerTreeNodeImpl)treeModel.getRoot());
+ }
+ }
+
+ private void updateTree(final TreeModel model, final DebuggerTreeNodeImpl node) {
+ if (node == null) {
+ return;
+ }
+ if (node.getDescriptor().myIsExpanded) {
+ final int count = model.getChildCount(node);
+ for (int idx = 0; idx < count; idx++) {
+ final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)model.getChild(node, idx);
+ updateTree(model, child);
+ }
+ }
+ node.labelChanged();
+ }
+ }
+
+ protected void setDebuggerContext(DebuggerContextImpl context) {
+ final PsiElement contextElement = PositionUtil.getContextElement(context);
+ myEditor.setContext(contextElement);
+ }
+
+ protected PsiElement getContext() {
+ return myEditor.getContext();
+ }
+
+ protected void initDialogData(TextWithImports text) {
+ getEditor().setText(text);
+ myEvaluationPanel.clear();
+ }
+
+ public DebuggerContextImpl getDebuggerContext() {
+ return DebuggerManagerEx.getInstanceEx(myProject).getContext();
+ }
+
+ public DebuggerEditorImpl getEditor() {
+ return myEditor;
+ }
+
+ protected abstract DebuggerEditorImpl createEditor(final CodeFragmentFactory factory);
+
+ protected MyEvaluationPanel getEvaluationPanel() {
+ return myEvaluationPanel;
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/EvaluatorRunnable.java b/java/debugger/impl/src/com/intellij/debugger/ui/EvaluatorRunnable.java
new file mode 100644
index 0000000..dda9fbb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/EvaluatorRunnable.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: May 28, 2003
+ * Time: 1:59:27 PM
+ * To change this template use Options | File Templates.
+ */
+public interface EvaluatorRunnable extends Runnable{
+ Object getValue();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/ExportDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/ExportDialog.java
new file mode 100644
index 0000000..535853d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/ExportDialog.java
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Dec 14, 2001
+ * @author Jeka
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.actions.ThreadDumpAction;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.help.HelpManager;
+import com.intellij.openapi.ide.CopyPasteManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.ScrollPaneFactory;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.List;
+
+public class ExportDialog extends DialogWrapper {
+ private final JTextArea myTextArea = new JTextArea();
+ private TextFieldWithBrowseButton myTfFilePath;
+ private final Project myProject;
+ private final DebugProcessImpl myDebugProcess;
+ private final CopyToClipboardAction myCopyToClipboardAction = new CopyToClipboardAction();
+ private static final @NonNls String DEFAULT_REPORT_FILE_NAME = "threads_report.txt";
+
+ public ExportDialog(DebugProcessImpl debugProcess, String destinationDirectory) {
+ super(debugProcess.getProject(), true);
+ myDebugProcess = debugProcess;
+ myProject = debugProcess.getProject();
+ setTitle(DebuggerBundle.message("threads.export.dialog.title"));
+ setOKButtonText(DebuggerBundle.message("button.save"));
+
+ init();
+
+ setOKActionEnabled(false);
+ myCopyToClipboardAction.setEnabled(false);
+
+ myTextArea.setText(MessageDescriptor.EVALUATING.getLabel());
+ debugProcess.getManagerThread().invoke(new ExportThreadsCommand(ApplicationManager.getApplication().getModalityStateForComponent(myTextArea)));
+
+ myTfFilePath.setText(destinationDirectory + File.separator + DEFAULT_REPORT_FILE_NAME);
+ setHorizontalStretch(1.5f);
+ }
+
+ protected Action[] createActions(){
+ return new Action[]{getOKAction(), myCopyToClipboardAction, getCancelAction(), getHelpAction()};
+ }
+
+ protected void doHelpAction() {
+ HelpManager.getInstance().invokeHelp(HelpID.EXPORT_THREADS);
+ }
+
+ protected JComponent createNorthPanel() {
+ JPanel box = new JPanel(new BorderLayout());
+ box.add(new JLabel(DebuggerBundle.message("label.threads.export.dialog.file")), BorderLayout.WEST);
+ myTfFilePath = new TextFieldWithBrowseButton();
+ myTfFilePath.addBrowseFolderListener(null, null, myProject, FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
+ box.add(myTfFilePath, BorderLayout.CENTER);
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(box, BorderLayout.CENTER);
+ panel.add(Box.createVerticalStrut(7), BorderLayout.SOUTH);
+ return panel;
+ }
+
+ protected JComponent createCenterPanel() {
+ myTextArea.setEditable(false);
+ JScrollPane pane = ScrollPaneFactory.createScrollPane(myTextArea);
+ pane.setPreferredSize(new Dimension(400, 300));
+ return pane;
+ }
+
+ protected void doOKAction() {
+ String path = myTfFilePath.getText();
+ File file = new File(path);
+ if (file.isDirectory()) {
+ Messages.showMessageDialog(
+ myProject,
+ DebuggerBundle.message("error.threads.export.dialog.file.is.directory"),
+ DebuggerBundle.message("threads.export.dialog.title"),
+ Messages.getErrorIcon()
+ );
+ }
+ else if (file.exists()) {
+ int answer = Messages.showYesNoDialog(
+ myProject,
+ DebuggerBundle.message("error.threads.export.dialog.file.already.exists", path),
+ DebuggerBundle.message("threads.export.dialog.title"),
+ Messages.getQuestionIcon()
+ );
+ if (answer == 0) {
+ super.doOKAction();
+ }
+ }
+ else {
+ super.doOKAction();
+ }
+ }
+
+ public String getFilePath() {
+ return myTfFilePath.getText();
+ }
+
+ public String getTextToSave() {
+ return myTextArea.getText();
+ }
+
+ protected String getDimensionServiceKey(){
+ return "#com.intellij.debugger.ui.ExportDialog";
+ }
+
+ public static String getExportThreadsText(VirtualMachineProxyImpl vmProxy) {
+ final StringBuffer buffer = new StringBuffer(512);
+ List<ThreadReference> threads = vmProxy.getVirtualMachine().allThreads();
+ for (ThreadReference threadReference : threads) {
+ final String name = threadName(threadReference);
+ if (name == null) {
+ continue;
+ }
+ buffer.append(name);
+ ReferenceType referenceType = threadReference.referenceType();
+ if (referenceType != null) {
+ //noinspection HardCodedStringLiteral
+ Field daemon = referenceType.fieldByName("daemon");
+ if (daemon != null) {
+ Value value = threadReference.getValue(daemon);
+ if (value instanceof BooleanValue && ((BooleanValue)value).booleanValue()) {
+ buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.daemon"));
+ }
+ }
+
+ //noinspection HardCodedStringLiteral
+ Field priority = referenceType.fieldByName("priority");
+ if (priority != null) {
+ Value value = threadReference.getValue(priority);
+ if (value instanceof IntegerValue) {
+ buffer.append(", ").append(DebuggerBundle.message("threads.export.attribute.label.priority", ((IntegerValue)value).intValue()));
+ }
+ }
+ }
+
+ ThreadGroupReference groupReference = threadReference.threadGroup();
+ if (groupReference != null) {
+ buffer.append(", ").append(DebuggerBundle.message("threads.export.attribute.label.group", groupReference.name()));
+ }
+ buffer.append(", ").append(
+ DebuggerBundle.message("threads.export.attribute.label.status", DebuggerUtilsEx.getThreadStatusText(threadReference.status())));
+
+ try {
+ if (vmProxy.canGetOwnedMonitorInfo() && vmProxy.canGetMonitorInfo()) {
+ List<ObjectReference> list = threadReference.ownedMonitors();
+ for (ObjectReference reference : list) {
+ final List<ThreadReference> waiting = reference.waitingThreads();
+ for (ThreadReference thread : waiting) {
+ final String waitingThreadName = threadName(thread);
+ if (waitingThreadName != null) {
+ buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.label.blocks.thread", waitingThreadName));
+ }
+ }
+ }
+ }
+
+ ObjectReference waitedMonitor = vmProxy.canGetCurrentContendedMonitor() ? threadReference.currentContendedMonitor() : null;
+ if (waitedMonitor != null) {
+ if (vmProxy.canGetMonitorInfo()) {
+ ThreadReference waitedThread = waitedMonitor.owningThread();
+ if (waitedThread != null) {
+ final String waitedThreadName = threadName(waitedThread);
+ if (waitedThreadName != null) {
+ buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.label.waiting.for.thread", waitedThreadName,
+ ThreadDumpAction.renderObject(waitedMonitor)));
+ }
+ }
+ }
+ }
+
+ final List<StackFrame> frames = threadReference.frames();
+ for (StackFrame stackFrame : frames) {
+ final Location location = stackFrame.location();
+ buffer.append("\n\t ").append(renderLocation(location));
+ }
+ }
+ catch (IncompatibleThreadStateException e) {
+ buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.error.incompatible.state"));
+ }
+ buffer.append("\n\n");
+ }
+ return buffer.toString();
+ }
+
+ private static String renderLocation(final Location location) {
+ String sourceName;
+ try {
+ sourceName = location.sourceName();
+ }
+ catch (AbsentInformationException e) {
+ sourceName = "Unknown Source";
+ }
+ return DebuggerBundle.message(
+ "export.threads.stackframe.format",
+ location.declaringType().name() + "." + location.method().name(),
+ sourceName,
+ location.lineNumber()
+ );
+ }
+
+ private static String threadName(ThreadReference threadReference) {
+ try {
+ return threadReference.name() + "@" + threadReference.uniqueID();
+ }
+ catch (ObjectCollectedException e) {
+ return null;
+ }
+ }
+
+ private class CopyToClipboardAction extends AbstractAction {
+ public CopyToClipboardAction() {
+ super(DebuggerBundle.message("button.copy"));
+ putValue(Action.SHORT_DESCRIPTION, DebuggerBundle.message("export.dialog.copy.action.description"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String s = StringUtil.convertLineSeparators(myTextArea.getText());
+ CopyPasteManager.getInstance().setContents(new StringSelection(s));
+ }
+ }
+
+ private class ExportThreadsCommand extends DebuggerCommandImpl {
+ protected ModalityState myModalityState;
+
+ public ExportThreadsCommand(ModalityState modalityState) {
+ myModalityState = modalityState;
+ }
+
+ private void setText(final String text) {
+ DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
+ public void run() {
+ myTextArea.setText(text);
+ setOKActionEnabled(true);
+ myCopyToClipboardAction.setEnabled(true);
+ }
+ }, myModalityState);
+ }
+
+ protected void action() {
+ setText(getExportThreadsText(myDebugProcess.getVirtualMachineProxy()));
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/ExpressionEvaluationDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/ExpressionEvaluationDialog.java
new file mode 100644
index 0000000..38bff61
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/ExpressionEvaluationDialog.java
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.actions.EvaluateActionHandler;
+import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.help.HelpManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+
+public class ExpressionEvaluationDialog extends EvaluationDialog {
+
+ public ExpressionEvaluationDialog(Project project, TextWithImports defaultExpression) {
+ super(project, defaultExpression);
+ setTitle(DebuggerBundle.message("evaluate.expression.dialog.title"));
+
+ final KeyStroke expressionStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.ALT_MASK);
+ final KeyStroke resultStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.ALT_MASK);
+
+ final JRootPane rootPane = getRootPane();
+
+ final AnAction anAction = new AnAction() {
+ public void actionPerformed(AnActionEvent e) {
+ getExpressionCombo().requestFocus();
+ }
+ };
+ anAction.registerCustomShortcutSet(new CustomShortcutSet(expressionStroke), rootPane);
+ addDisposeRunnable(new Runnable() {
+ public void run() {
+ anAction.unregisterCustomShortcutSet(rootPane);
+ }
+ });
+
+ final AnAction anAction2 = new AnAction() {
+ public void actionPerformed(AnActionEvent e) {
+ getEvaluationPanel().getWatchTree().requestFocus();
+ }
+ };
+ anAction2.registerCustomShortcutSet(new CustomShortcutSet(resultStroke), rootPane);
+ addDisposeRunnable(new Runnable() {
+ public void run() {
+ anAction2.unregisterCustomShortcutSet(rootPane);
+ }
+ });
+
+ init();
+ }
+
+ protected DebuggerExpressionComboBox createEditor(final CodeFragmentFactory factory) {
+ return new DebuggerExpressionComboBox(getProject(), PositionUtil.getContextElement(getDebuggerContext()), "evaluation", factory);
+ }
+
+ protected JComponent createCenterPanel() {
+ final JPanel panel = new JPanel(new BorderLayout());
+
+ final JPanel exprPanel = new JPanel(new BorderLayout(UIUtil.DEFAULT_HGAP, 0));
+ exprPanel.add(new JLabel(DebuggerBundle.message("label.evaluate.dialog.expression")), BorderLayout.WEST);
+ exprPanel.add(getExpressionCombo(), BorderLayout.CENTER);
+ final JBLabel help = new JBLabel("Press Enter to Evaluate or Control+Enter to evaluate and add to the Watches", SwingConstants.RIGHT);
+ help.setBorder(IdeBorderFactory.createEmptyBorder(2,0,6,0));
+ help.setComponentStyle(UIUtil.ComponentStyle.SMALL);
+ help.setFontColor(UIUtil.FontColor.BRIGHTER);
+ exprPanel.add(help, BorderLayout.SOUTH);
+
+
+ final JPanel resultPanel = new JPanel(new BorderLayout());
+ //resultPanel.add(new JLabel(DebuggerBundle.message("label.evaluate.dialog.result")), BorderLayout.NORTH);
+ resultPanel.add(getEvaluationPanel(), BorderLayout.CENTER);
+
+ panel.add(exprPanel, BorderLayout.NORTH);
+ panel.add(resultPanel, BorderLayout.CENTER);
+
+ return panel;
+ }
+
+ protected void initDialogData(TextWithImports text) {
+ super.initDialogData(text);
+ getExpressionCombo().selectAll();
+ }
+
+ private DebuggerExpressionComboBox getExpressionCombo() {
+ return (DebuggerExpressionComboBox)getEditor();
+ }
+
+ protected Action[] createActions() {
+ return new Action[] { getOKAction(), getCancelAction(), new SwitchAction(), getHelpAction() } ;
+ }
+
+ @Override
+ protected void createDefaultActions() {
+ super.createDefaultActions();
+ myOKAction = new OkAction(){
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) {
+ addCurrentExpressionToWatches();
+ }
+ }
+ };
+ }
+
+ private void addCurrentExpressionToWatches() {
+ final DebuggerSessionTab tab = DebuggerPanelsManager.getInstance(getProject()).getSessionTab();
+ if (tab != null) {
+ final TextWithImports evaluate = getCodeToEvaluate();
+ if (evaluate != null) {
+ tab.getWatchPanel().getWatchTree().addWatch(evaluate, null);
+ }
+ }
+ }
+
+ protected void doHelpAction() {
+ HelpManager.getInstance().invokeHelp(HelpID.EVALUATE);
+ }
+
+ private class SwitchAction extends AbstractAction {
+ public SwitchAction() {
+ putValue(Action.NAME, DebuggerBundle.message("action.evaluate.expression.dialog.switch.mode.description"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ final TextWithImports text = getEditor().getText();
+ doCancelAction();
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ EvaluateActionHandler.showEvaluationDialog(getProject(), text, DebuggerSettings.EVALUATE_FRAGMENT);
+ }
+ });
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java
new file mode 100644
index 0000000..1a137e9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java
@@ -0,0 +1,691 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.SuspendManagerUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextUtil;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.impl.DebuggerComboBoxRenderer;
+import com.intellij.debugger.ui.impl.FramesList;
+import com.intellij.debugger.ui.impl.UpdatableDebuggerView;
+import com.intellij.debugger.ui.impl.watch.MethodsTracker;
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.OccurenceNavigator;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBoxWithWidePopup;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.CaptionPanel;
+import com.intellij.ui.PopupHandler;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.border.CustomLineBorder;
+import com.intellij.ui.components.panels.Wrapper;
+import com.intellij.util.Alarm;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.VMDisconnectedException;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class FramesPanel extends UpdatableDebuggerView {
+ private static final Icon FILTER_STACK_FRAMES_ICON = AllIcons.Debugger.Class_filter;
+
+ private final JComboBox myThreadsCombo;
+ private final FramesList myFramesList;
+ private final ThreadsListener myThreadsListener;
+ private final FramesListener myFramesListener;
+ private final DebuggerStateManager myStateManager;
+ private boolean myShowLibraryFrames = DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES;
+ private final Alarm myRebuildAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+
+ public FramesPanel(Project project, DebuggerStateManager stateManager) {
+ super(project, stateManager);
+ myStateManager = stateManager;
+
+ setLayout(new BorderLayout());
+
+ myThreadsCombo = new ComboBoxWithWidePopup();
+ myThreadsCombo.setRenderer(new DebuggerComboBoxRenderer(myThreadsCombo.getRenderer()));
+ myThreadsListener = new ThreadsListener();
+ myThreadsCombo.addItemListener(myThreadsListener);
+
+ myFramesList = new FramesList(project);
+ myFramesListener = new FramesListener();
+ myFramesList.addListSelectionListener(myFramesListener);
+
+ myFramesList.addMouseListener(new MouseAdapter() {
+ public void mousePressed(final MouseEvent e) {
+ int index = myFramesList.locationToIndex(e.getPoint());
+ if (index >= 0 && myFramesList.isSelectedIndex(index)) {
+ processListValue(myFramesList.getModel().getElementAt(index));
+ }
+ }
+ });
+
+ registerThreadsPopupMenu(myFramesList);
+
+ setBorder(null);
+
+ final ActionToolbar toolbar = createToolbar();
+ Wrapper threads = new Wrapper();
+ CustomLineBorder border = new CustomLineBorder(CaptionPanel.CNT_ACTIVE_BORDER_COLOR, 0, 0, 1, 0);
+ threads.setBorder(border);
+ threads.add(toolbar.getComponent(), BorderLayout.EAST);
+ threads.add(myThreadsCombo, BorderLayout.CENTER);
+ add(threads, BorderLayout.NORTH);
+ add(ScrollPaneFactory.createScrollPane(myFramesList), BorderLayout.CENTER);
+ }
+
+ private ActionToolbar createToolbar() {
+ final DefaultActionGroup framesGroup = new DefaultActionGroup();
+ framesGroup.addSeparator();
+
+ CommonActionsManager actionsManager = CommonActionsManager.getInstance();
+ framesGroup.add(actionsManager.createPrevOccurenceAction(getOccurenceNavigator()));
+ framesGroup.add(actionsManager.createNextOccurenceAction(getOccurenceNavigator()));
+ framesGroup.add(new ShowLibraryFramesAction());
+
+ final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.DEBUGGER_TOOLBAR, framesGroup, true);
+ toolbar.setReservePlaceAutoPopupIcon(false);
+ ((ActionToolbarImpl)toolbar).setAddSeparatorFirst(true);
+ toolbar.getComponent().setBorder(new EmptyBorder(1, 0, 0, 0));
+ return toolbar;
+ }
+
+ public DebuggerStateManager getContextManager() {
+ return myStateManager;
+ }
+
+ private class FramesListener implements ListSelectionListener {
+ boolean myIsEnabled = true;
+
+ public void setEnabled(boolean enabled) {
+ myIsEnabled = enabled;
+ }
+
+ public void valueChanged(ListSelectionEvent e) {
+ if (!myIsEnabled || e.getValueIsAdjusting()) {
+ return;
+ }
+ final JList list = (JList)e.getSource();
+ processListValue(list.getSelectedValue());
+ }
+
+ }
+ private void processListValue(final Object selected) {
+ if (selected instanceof StackFrameDescriptorImpl) {
+ DebuggerContextUtil.setStackFrame(getContextManager(), ((StackFrameDescriptorImpl)selected).getFrameProxy());
+ }
+ }
+
+
+ private void registerThreadsPopupMenu(final JList framesList) {
+ final PopupHandler popupHandler = new PopupHandler() {
+ public void invokePopup(Component comp, int x, int y) {
+ DefaultActionGroup group = (DefaultActionGroup)ActionManager.getInstance().getAction(DebuggerActions.THREADS_PANEL_POPUP);
+ ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(DebuggerActions.THREADS_PANEL_POPUP, group);
+ popupMenu.getComponent().show(comp, x, y);
+ }
+ };
+ framesList.addMouseListener(popupHandler);
+ registerDisposable(new Disposable() {
+ public void dispose() {
+ myThreadsCombo.removeItemListener(myThreadsListener);
+ framesList.removeMouseListener(popupHandler);
+ }
+ });
+ }
+
+ private class ThreadsListener implements ItemListener {
+ boolean myIsEnabled = true;
+
+ public void setEnabled(boolean enabled) {
+ myIsEnabled = enabled;
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ if (!myIsEnabled) return;
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ ThreadDescriptorImpl item = (ThreadDescriptorImpl)e.getItem();
+ DebuggerContextUtil.setThread(getContextManager(), item);
+ }
+ }
+ }
+
+ private final AtomicBoolean myPerformFullRebuild = new AtomicBoolean(false);
+
+ protected void rebuild(int event) {
+ myRebuildAlarm.cancelAllRequests();
+ final boolean isRefresh = event == DebuggerSession.EVENT_REFRESH ||
+ event == DebuggerSession.EVENT_REFRESH_VIEWS_ONLY ||
+ event == DebuggerSession.EVENT_THREADS_REFRESH;
+ if (!isRefresh) {
+ myPerformFullRebuild.set(true);
+ }
+ myRebuildAlarm.addRequest(new Runnable() {
+ public void run() {
+ try {
+ doRebuild(!myPerformFullRebuild.getAndSet(false));
+ }
+ catch (VMDisconnectedException e) {
+ // ignored
+ }
+ }
+ }, 100, ModalityState.NON_MODAL);
+ }
+
+ private void doRebuild(boolean refreshOnly) {
+ final DebuggerContextImpl context = getContext();
+ final boolean paused = context.getDebuggerSession().isPaused();
+ if (!paused || !refreshOnly) {
+ myThreadsCombo.removeAllItems();
+ synchronized (myFramesList) {
+ myFramesLastUpdateTime = getNextStamp();
+ myFramesList.getModel().clear();
+ }
+ }
+
+ if (paused) {
+ final DebugProcessImpl process = context.getDebugProcess();
+ if (process != null) {
+ process.getManagerThread().schedule(new RefreshFramePanelCommand(refreshOnly && myThreadsCombo.getItemCount() != 0));
+ }
+ }
+ }
+
+ @Override
+ public void dispose() {
+ try {
+ Disposer.dispose(myRebuildAlarm);
+ }
+ finally {
+ super.dispose();
+ }
+ }
+
+ public boolean isShowLibraryFrames() {
+ return myShowLibraryFrames;
+ }
+
+ public void setShowLibraryFrames(boolean showLibraryFrames) {
+ if (myShowLibraryFrames != showLibraryFrames) {
+ myShowLibraryFrames = showLibraryFrames;
+ rebuild(DebuggerSession.EVENT_CONTEXT);
+ }
+ }
+
+ private class RefreshFramePanelCommand extends DebuggerContextCommandImpl {
+ private final boolean myRefreshOnly;
+ private final ThreadDescriptorImpl[] myThreadDescriptorsToUpdate;
+
+ public RefreshFramePanelCommand(final boolean refreshOnly) {
+ super(getContext());
+ myRefreshOnly = refreshOnly;
+ if (refreshOnly) {
+ final int size = myThreadsCombo.getItemCount();
+ myThreadDescriptorsToUpdate = new ThreadDescriptorImpl[size];
+ for (int idx = 0; idx < size; idx++) {
+ myThreadDescriptorsToUpdate[idx] = (ThreadDescriptorImpl)myThreadsCombo.getItemAt(idx);
+ }
+ }
+ else {
+ myThreadDescriptorsToUpdate = null;
+ }
+ }
+
+ private List<ThreadDescriptorImpl> createThreadDescriptorsList() {
+ final List<ThreadReferenceProxyImpl> threads = new ArrayList<ThreadReferenceProxyImpl>(getSuspendContext().getDebugProcess().getVirtualMachineProxy().allThreads());
+ Collections.sort(threads, ThreadReferenceProxyImpl.ourComparator);
+
+ final List<ThreadDescriptorImpl> descriptors = new ArrayList<ThreadDescriptorImpl>(threads.size());
+ EvaluationContextImpl evaluationContext = getDebuggerContext().createEvaluationContext();
+
+ for (ThreadReferenceProxyImpl thread : threads) {
+ ThreadDescriptorImpl threadDescriptor = new ThreadDescriptorImpl(thread);
+ threadDescriptor.setContext(evaluationContext);
+ threadDescriptor.updateRepresentation(evaluationContext, DescriptorLabelListener.DUMMY_LISTENER);
+ descriptors.add(threadDescriptor);
+ }
+ return descriptors;
+ }
+
+ public void threadAction() {
+ if (myRefreshOnly && myThreadDescriptorsToUpdate.length != myThreadsCombo.getItemCount()) {
+ // there is no sense in refreshing combobox if thread list has changed since creation of this command
+ return;
+ }
+
+ final DebuggerContextImpl context = getDebuggerContext();
+
+ final ThreadReferenceProxyImpl threadToSelect = context.getThreadProxy();
+ if(threadToSelect == null) {
+ return;
+ }
+
+ final SuspendContextImpl threadContext = SuspendManagerUtil.getSuspendContextForThread(context.getSuspendContext(), threadToSelect);
+ final ThreadDescriptorImpl currentThreadDescriptor = (ThreadDescriptorImpl)myThreadsCombo.getSelectedItem();
+ final ThreadReferenceProxyImpl currentThread = currentThreadDescriptor != null? currentThreadDescriptor.getThreadReference() : null;
+
+ if (myRefreshOnly && threadToSelect.equals(currentThread)) {
+ context.getDebugProcess().getManagerThread().schedule(new UpdateFramesListCommand(context, threadContext));
+ }
+ else {
+ context.getDebugProcess().getManagerThread().schedule(new RebuildFramesListCommand(context, threadContext));
+ }
+
+ if (myRefreshOnly) {
+ final EvaluationContextImpl evaluationContext = context.createEvaluationContext();
+ for (ThreadDescriptorImpl descriptor : myThreadDescriptorsToUpdate) {
+ descriptor.setContext(evaluationContext);
+ descriptor.updateRepresentation(evaluationContext, DescriptorLabelListener.DUMMY_LISTENER);
+ }
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ try {
+ myThreadsListener.setEnabled(false);
+ selectThread(threadToSelect);
+ myFramesList.repaint();
+ }
+ finally {
+ myThreadsListener.setEnabled(true);
+ }
+ }
+ });
+ }
+ else { // full rebuild
+ refillThreadsCombo(threadToSelect);
+ }
+ }
+
+ protected void commandCancelled() {
+ if (!DebuggerManagerThreadImpl.isManagerThread()) {
+ return;
+ }
+ // context thread is not suspended
+ final DebuggerContextImpl context = getDebuggerContext();
+
+ final SuspendContextImpl suspendContext = context.getSuspendContext();
+ if (suspendContext == null) {
+ return;
+ }
+ final ThreadReferenceProxyImpl threadToSelect = context.getThreadProxy();
+ if(threadToSelect == null) {
+ return;
+ }
+
+ if (!suspendContext.isResumed()) {
+ final SuspendContextImpl threadContext = SuspendManagerUtil.getSuspendContextForThread(suspendContext, threadToSelect);
+ context.getDebugProcess().getManagerThread().schedule(new RebuildFramesListCommand(context, threadContext));
+ refillThreadsCombo(threadToSelect);
+ }
+ }
+
+ private void refillThreadsCombo(final ThreadReferenceProxyImpl threadToSelect) {
+ final List<ThreadDescriptorImpl> threadItems = createThreadDescriptorsList();
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ try {
+ myThreadsListener.setEnabled(false);
+
+ myThreadsCombo.removeAllItems();
+ for (final ThreadDescriptorImpl threadItem : threadItems) {
+ myThreadsCombo.addItem(threadItem);
+ }
+
+ selectThread(threadToSelect);
+ }
+ finally {
+ myThreadsListener.setEnabled(true);
+ }
+ }
+ });
+ }
+
+ }
+
+ private class UpdateFramesListCommand extends SuspendContextCommandImpl {
+ private final DebuggerContextImpl myDebuggerContext;
+
+ public UpdateFramesListCommand(DebuggerContextImpl debuggerContext, SuspendContextImpl suspendContext) {
+ super(suspendContext);
+ myDebuggerContext = debuggerContext;
+ }
+
+ public void contextAction() throws Exception {
+ updateFrameList(myDebuggerContext.getThreadProxy());
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ try {
+ myFramesListener.setEnabled(false);
+ final StackFrameProxyImpl contextFrame = getDebuggerContext().getFrameProxy();
+ if(contextFrame != null) {
+ selectFrame(contextFrame);
+ }
+ }
+ finally {
+ myFramesListener.setEnabled(true);
+ }
+ }
+ });
+
+ }
+
+ private void updateFrameList(ThreadReferenceProxyImpl thread) {
+ try {
+ if(!getSuspendContext().getDebugProcess().getSuspendManager().isSuspended(thread)) {
+ return;
+ }
+ }
+ catch (ObjectCollectedException e) {
+ return;
+ }
+
+ final EvaluationContextImpl evaluationContext = getDebuggerContext().createEvaluationContext();
+ final List<StackFrameDescriptorImpl> descriptors = new ArrayList<StackFrameDescriptorImpl>();
+
+ synchronized (myFramesList) {
+ final DefaultListModel model = myFramesList.getModel();
+ final int size = model.getSize();
+ for (int i = 0; i < size; i++) {
+ final Object elem = model.getElementAt(i);
+ if (elem instanceof StackFrameDescriptorImpl) {
+ descriptors.add((StackFrameDescriptorImpl)elem);
+ }
+ }
+ }
+
+ for (StackFrameDescriptorImpl descriptor : descriptors) {
+ descriptor.setContext(evaluationContext);
+ descriptor.updateRepresentation(evaluationContext, DescriptorLabelListener.DUMMY_LISTENER);
+ }
+ }
+
+ public DebuggerContextImpl getDebuggerContext() {
+ return myDebuggerContext;
+ }
+ }
+
+ private class RebuildFramesListCommand extends SuspendContextCommandImpl {
+ private final DebuggerContextImpl myDebuggerContext;
+
+ public RebuildFramesListCommand(DebuggerContextImpl debuggerContext, SuspendContextImpl suspendContext) {
+ super(suspendContext);
+ myDebuggerContext = debuggerContext;
+ }
+
+ public void contextAction() throws Exception {
+ final ThreadReferenceProxyImpl thread = myDebuggerContext.getThreadProxy();
+ try {
+ if(!getSuspendContext().getDebugProcess().getSuspendManager().isSuspended(thread)) {
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ try {
+ myFramesListener.setEnabled(false);
+ synchronized (myFramesList) {
+ myFramesLastUpdateTime = getNextStamp();
+ final DefaultListModel model = myFramesList.getModel();
+ model.clear();
+ model.addElement(new Object() {
+ public String toString() {
+ return DebuggerBundle.message("frame.panel.frames.not.available");
+ }
+ });
+ myFramesList.setSelectedIndex(0);
+ }
+ }
+ finally {
+ myFramesListener.setEnabled(true);
+ }
+ }
+ });
+
+ return;
+ }
+ }
+ catch (ObjectCollectedException e) {
+ return;
+ }
+
+ List<StackFrameProxyImpl> frames;
+ try {
+ frames = thread.frames();
+ }
+ catch (EvaluateException e) {
+ frames = Collections.emptyList();
+ }
+
+ final StackFrameProxyImpl contextFrame = myDebuggerContext.getFrameProxy();
+ final EvaluationContextImpl evaluationContext = myDebuggerContext.createEvaluationContext();
+ final DebuggerManagerThreadImpl managerThread = myDebuggerContext.getDebugProcess().getManagerThread();
+ final MethodsTracker tracker = new MethodsTracker();
+ final int totalFramesCount = frames.size();
+ int index = 0;
+ final IndexCounter indexCounter = new IndexCounter(totalFramesCount);
+ final long timestamp = getNextStamp();
+ for (StackFrameProxyImpl stackFrameProxy : frames) {
+ managerThread.schedule(
+ new AppendFrameCommand(
+ getSuspendContext(),
+ stackFrameProxy,
+ evaluationContext,
+ tracker,
+ index++,
+ stackFrameProxy.equals(contextFrame),
+ timestamp,
+ indexCounter
+ )
+ );
+ }
+ }
+ }
+
+ private void selectThread(ThreadReferenceProxyImpl toSelect) {
+ int count = myThreadsCombo.getItemCount();
+ for (int idx = 0; idx < count; idx++) {
+ ThreadDescriptorImpl item = (ThreadDescriptorImpl)myThreadsCombo.getItemAt(idx);
+ if (toSelect.equals(item.getThreadReference())) {
+ if (!item.equals(myThreadsCombo.getSelectedItem())) {
+ myThreadsCombo.setSelectedIndex(idx);
+ }
+ return;
+ }
+ }
+ }
+
+ /*invoked in swing thread*/
+ private void selectFrame(StackFrameProxy frame) {
+ synchronized (myFramesList) {
+ final int count = myFramesList.getElementCount();
+ final Object selectedValue = myFramesList.getSelectedValue();
+ final DefaultListModel model = myFramesList.getModel();
+ for (int idx = 0; idx < count; idx++) {
+ final Object elem = model.getElementAt(idx);
+ if (elem instanceof StackFrameDescriptorImpl) {
+ final StackFrameDescriptorImpl item = (StackFrameDescriptorImpl)elem;
+ if (frame.equals(item.getFrameProxy())) {
+ if (!item.equals(selectedValue)) {
+ myFramesList.setSelectedIndex(idx);
+ }
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ private static class IndexCounter {
+ private final int[] myData;
+
+ private IndexCounter(int totalSize) {
+ myData = new int[totalSize];
+ for (int idx = 0; idx < totalSize; idx++) {
+ myData[idx] = 0;
+ }
+ }
+
+ public void markCalculated(int idx){
+ myData[idx] = 1;
+ }
+
+ public int getActualIndex(final int index) {
+ int result = 0;
+ for (int idx = 0; idx < index; idx++) {
+ result += myData[idx];
+ }
+ return result;
+ }
+ }
+
+ private final AtomicLong myTimeCounter = new AtomicLong(0L);
+ private long getNextStamp() {
+ return myTimeCounter.incrementAndGet();
+ }
+
+ private long myFramesLastUpdateTime = 0L;
+
+ private class AppendFrameCommand extends SuspendContextCommandImpl {
+ private final StackFrameProxyImpl myFrame;
+ private final EvaluationContextImpl myEvaluationContext;
+ private final MethodsTracker myTracker;
+ private final int myIndexToInsert;
+ private final boolean myIsContextFrame;
+ private final long myTimestamp;
+ private final IndexCounter myCounter;
+
+ public AppendFrameCommand(SuspendContextImpl suspendContext, StackFrameProxyImpl frame, EvaluationContextImpl evaluationContext,
+ MethodsTracker tracker, int indexToInsert, final boolean isContextFrame, final long timestamp, IndexCounter counter) {
+ super(suspendContext);
+ myFrame = frame;
+ myEvaluationContext = evaluationContext;
+ myTracker = tracker;
+ myIndexToInsert = indexToInsert;
+ myIsContextFrame = isContextFrame;
+ myTimestamp = timestamp;
+ myCounter = counter;
+ }
+
+ public void contextAction() throws Exception {
+ final StackFrameDescriptorImpl descriptor = new StackFrameDescriptorImpl(myFrame, myTracker);
+ descriptor.setContext(myEvaluationContext);
+ descriptor.updateRepresentation(myEvaluationContext, DescriptorLabelListener.DUMMY_LISTENER);
+ final Project project = getProject();
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ public void run() {
+ try {
+ myFramesListener.setEnabled(false);
+ synchronized (myFramesList) {
+ final DefaultListModel model = myFramesList.getModel();
+ if (myFramesLastUpdateTime < myTimestamp) {
+ myFramesLastUpdateTime = myTimestamp;
+ model.clear();
+ }
+ if (myTimestamp != myFramesLastUpdateTime) {
+ return; // the command has expired
+ }
+ final boolean shouldHide = !myShowLibraryFrames && !myIsContextFrame && myIndexToInsert != 0 && (descriptor.isSynthetic() || descriptor.isInLibraryContent());
+ if (!shouldHide) {
+ myCounter.markCalculated(myIndexToInsert);
+ final int actualIndex = myCounter.getActualIndex(myIndexToInsert);
+ model.insertElementAt(descriptor, actualIndex);
+ if (myIsContextFrame) {
+ myFramesList.setSelectedIndex(actualIndex);
+ }
+ }
+ }
+ }
+ finally {
+ myFramesListener.setEnabled(true);
+ }
+ }
+ });
+ }
+ }
+
+ public void requestFocus() {
+ myFramesList.requestFocus();
+ }
+
+ public OccurenceNavigator getOccurenceNavigator() {
+ return myFramesList;
+ }
+
+ public FramesList getFramesList() {
+ return myFramesList;
+ }
+
+ private class ShowLibraryFramesAction extends ToggleAction {
+ private volatile boolean myShouldShow;
+ private static final String ourTextWhenShowIsOn = "Hide Frames from Libraries";
+ private static final String ourTextWhenShowIsOff = "Show All Frames";
+
+ public ShowLibraryFramesAction() {
+ super("", "", FILTER_STACK_FRAMES_ICON);
+ myShouldShow = DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES;
+ }
+
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ final boolean shouldShow = !(Boolean)presentation.getClientProperty(SELECTED_PROPERTY);
+ presentation.setText(shouldShow ? ourTextWhenShowIsOn : ourTextWhenShowIsOff);
+ }
+
+ public boolean isSelected(AnActionEvent e) {
+ return !myShouldShow;
+ }
+
+ public void setSelected(AnActionEvent e, boolean enabled) {
+ myShouldShow = !enabled;
+ DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES = myShouldShow;
+ setShowLibraryFrames(myShouldShow);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/GenericDebuggerParametersPatcherConfigurable.form b/java/debugger/impl/src/com/intellij/debugger/ui/GenericDebuggerParametersPatcherConfigurable.form
new file mode 100644
index 0000000..01cb795
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/GenericDebuggerParametersPatcherConfigurable.form
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.impl.GenericDebuggerParametersRunnerConfigurable">
+ <grid id="a1cd4" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="53" y="171" width="441" height="92"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="86bed" binding="myShMemPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="9f41f" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.generic.debugger.parameters.patcher.configurable.shmem.address"/>
+ </properties>
+ </component>
+ <component id="af504" class="javax.swing.JTextField" binding="myAddressField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="c2b5" binding="myPortPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="6e48a" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.generic.debugger.parameters.patcher.configurable.port"/>
+ </properties>
+ </component>
+ <component id="5ffe7" class="javax.swing.JTextField" binding="myPortField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="aad8d" class="javax.swing.JButton" binding="myDebuggerSettings">
+ <constraints>
+ <grid row="1" column="1" row-span="2" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="button.debugger.settings"/>
+ </properties>
+ </component>
+ <grid id="b10c7" binding="myTransportPanel" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="63d09" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.generic.debugger.parameters.patcher.configurable.transport"/>
+ </properties>
+ </component>
+ <component id="f3bb4" class="javax.swing.JRadioButton" binding="mySocketTransport">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <selected value="true"/>
+ <text resource-bundle="messages/DebuggerBundle" key="label.generic.debugger.parameters.patcher.configurable.socket"/>
+ </properties>
+ </component>
+ <component id="3bb61" class="javax.swing.JRadioButton" binding="myShmemTransport">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.generic.debugger.parameters.patcher.configurable.shmem"/>
+ </properties>
+ </component>
+ <hspacer id="b3e0f">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/GetJPDADialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/GetJPDADialog.java
new file mode 100644
index 0000000..c88c008
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/GetJPDADialog.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Oct 16, 2002
+ * Time: 5:31:46 PM
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.ide.BrowserUtil;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.ui.DoubleClickListener;
+import com.intellij.ui.JBColor;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+
+public class GetJPDADialog extends DialogWrapper {
+ private static final @NonNls String JPDA_URL = "http://java.sun.com/products/jpda";
+
+ public GetJPDADialog() {
+ super(false);
+ setTitle(DebuggerBundle.message("get.jpda.dialog.title"));
+ setResizable(false);
+ init();
+ }
+
+ protected Action[] createActions() {
+ return new Action[]{getOKAction()};
+ }
+
+ protected JComponent createCenterPanel() {
+ final JPanel _panel1 = new JPanel(new BorderLayout());
+
+ JPanel _panel2 = new JPanel(new BorderLayout());
+ _panel2.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ //"Debug libraries are missig from JDK home.\nIn order for debugger to start, the libraries should be installed.\nPlease visit http://java.sun.com/products/jpda"
+ JLabel label1 = new JLabel(DebuggerBundle.message("label.get.jpda.dialog.prompt"));
+ //label1.setForeground(Color.black);
+ JLabel label2 = new JLabel(JPDA_URL);
+ new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ BrowserUtil.launchBrowser(JPDA_URL);
+ return true;
+ }
+ }.installOn(label2);
+ label2.setForeground(JBColor.BLUE.darker());
+ label2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ _panel2.add(new JLabel(DebuggerBundle.message("label.get.jpda.dialog.error.description")), BorderLayout.NORTH);
+ _panel2.add(label1, BorderLayout.WEST);
+ _panel2.add(label2, BorderLayout.EAST);
+ _panel1.add(_panel2, BorderLayout.NORTH);
+
+ JPanel content = new JPanel(new GridLayout(2, 1, 10, 10));
+
+ _panel1.add(content, BorderLayout.CENTER);
+ return _panel1;
+ }
+}
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapProgressImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapProgressImpl.java
new file mode 100644
index 0000000..b2b0091
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapProgressImpl.java
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.HotSwapProgress;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.progress.PerformInBackgroundOption;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
+import com.intellij.openapi.progress.util.ProgressWindow;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.ui.MessageCategory;
+import gnu.trove.TIntObjectHashMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class HotSwapProgressImpl extends HotSwapProgress{
+ static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("HotSwap", ToolWindowId.DEBUG, true);
+
+ TIntObjectHashMap<List<String>> myMessages = new TIntObjectHashMap<List<String>>();
+ private final ProgressWindow myProgressWindow;
+ private String myTitle = DebuggerBundle.message("progress.hot.swap.title");
+
+ public HotSwapProgressImpl(Project project) {
+ super(project);
+ myProgressWindow = new BackgroundableProcessIndicator(getProject(), myTitle, new PerformInBackgroundOption() {
+ public boolean shouldStartInBackground() {
+ return DebuggerSettings.getInstance().HOTSWAP_IN_BACKGROUND;
+ }
+
+ public void processSentToBackground() {
+ }
+
+ }, null, null, true) {
+ public void cancel() {
+ HotSwapProgressImpl.this.cancel();
+ super.cancel();
+ }
+ };
+ }
+
+ public void finished() {
+ super.finished();
+
+ final List<String> errors = getMessages(MessageCategory.ERROR);
+ final List<String> warnings = getMessages(MessageCategory.WARNING);
+ if (!errors.isEmpty()) {
+ NOTIFICATION_GROUP.createNotification(DebuggerBundle.message("status.hot.swap.completed.with.errors"), buildMessage(errors),
+ NotificationType.ERROR, null).notify(getProject());
+ }
+ else if (!warnings.isEmpty()){
+ NOTIFICATION_GROUP.createNotification(DebuggerBundle.message("status.hot.swap.completed.with.warnings"),
+ buildMessage(warnings), NotificationType.WARNING, null).notify(getProject());
+ }
+ else if (!myMessages.isEmpty()){
+ List<String> messages = new ArrayList<String>();
+ for (int category : myMessages.keys()) {
+ messages.addAll(getMessages(category));
+ }
+ NOTIFICATION_GROUP.createNotification(buildMessage(messages), NotificationType.INFORMATION).notify(getProject());
+ }
+ }
+
+ private List<String> getMessages(int category) {
+ final List<String> messages = myMessages.get(category);
+ return messages == null? Collections.<String>emptyList() : messages;
+ }
+
+ private static String buildMessage(List<String> messages) {
+ return StringUtil.trimEnd(StringUtil.join(messages, " \n").trim(), ";");
+ }
+
+ public void addMessage(DebuggerSession session, final int type, final String text) {
+ List<String> messages = myMessages.get(type);
+ if (messages == null) {
+ messages = new ArrayList<String>();
+ myMessages.put(type, messages);
+ }
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(session.getSessionName()).append(": ").append(text).append(";");
+ messages.add(builder.toString());
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+
+ public void setText(final String text) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ if (!myProgressWindow.isCanceled() && myProgressWindow.isRunning()) {
+ myProgressWindow.setText(text);
+ }
+ }
+ }, myProgressWindow.getModalityState());
+
+ }
+
+ public void setTitle(final String text) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ if (!myProgressWindow.isCanceled() && myProgressWindow.isRunning()) {
+ myProgressWindow.setTitle(text);
+ }
+ }
+ }, myProgressWindow.getModalityState());
+
+ }
+
+ public void setFraction(final double v) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ if (!myProgressWindow.isCanceled() && myProgressWindow.isRunning()) {
+ myProgressWindow.setFraction(v);
+ }
+ }
+ }, myProgressWindow.getModalityState());
+ }
+
+ public boolean isCancelled() {
+ return myProgressWindow.isCanceled();
+ }
+
+ public ProgressIndicator getProgressIndicator() {
+ return myProgressWindow;
+ }
+
+ public void setDebuggerSession(DebuggerSession session) {
+ myTitle = DebuggerBundle.message("progress.hot.swap.title") + " : " + session.getSessionName();
+ myProgressWindow.setTitle(myTitle);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java
new file mode 100644
index 0000000..5cae3a8
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package com.intellij.debugger.ui;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.debugger.impl.DebuggerSession;
+
+/**
+ * @author nik
+ */
+public abstract class HotSwapUI {
+ public static HotSwapUI getInstance(Project project) {
+ return project.getComponent(HotSwapUI.class);
+ }
+
+ public abstract void reloadChangedClasses(DebuggerSession session, boolean compileBeforeHotswap);
+
+ public abstract void dontPerformHotswapAfterThisCompilation();
+
+
+ public abstract void addListener(HotSwapVetoableListener listener);
+
+ public abstract void removeListener(HotSwapVetoableListener listener);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java
new file mode 100644
index 0000000..7683bc1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.CommonBundle;
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManager;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.impl.DebuggerManagerAdapter;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.HotSwapFile;
+import com.intellij.debugger.impl.HotSwapManager;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.compiler.CompilationStatusListener;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.compiler.CompilerTopics;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.util.PairFunction;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.messages.MessageBus;
+import com.intellij.util.messages.MessageBusConnection;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * User: lex
+ * Date: Oct 2, 2003
+ * Time: 6:00:55 PM
+ */
+public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent {
+ private final List<HotSwapVetoableListener> myListeners = ContainerUtil.createEmptyCOWList();
+ private boolean myAskBeforeHotswap = true;
+ private final Project myProject;
+ private boolean myPerformHotswapAfterThisCompilation = true;
+
+ public HotSwapUIImpl(final Project project, final MessageBus bus, DebuggerManager debugManager) {
+ myProject = project;
+
+ ((DebuggerManagerEx)debugManager).addDebuggerManagerListener(new DebuggerManagerAdapter() {
+ private MessageBusConnection myConn = null;
+ private int mySessionCount = 0;
+
+ @Override
+ public void sessionAttached(DebuggerSession session) {
+ if (mySessionCount++ == 0) {
+ myConn = bus.connect();
+ myConn.subscribe(CompilerTopics.COMPILATION_STATUS, new MyCompilationStatusListener());
+ }
+ }
+
+ @Override
+ public void sessionDetached(DebuggerSession session) {
+ mySessionCount = Math.max(0, mySessionCount - 1);
+ if (mySessionCount == 0) {
+ final MessageBusConnection conn = myConn;
+ if (conn != null) {
+ Disposer.dispose(conn);
+ myConn = null;
+ }
+ }
+ }
+ });
+ }
+
+ public void projectOpened() {
+ }
+
+ public void projectClosed() {
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "HotSwapUI";
+ }
+
+ public void initComponent() {
+ }
+
+ public void disposeComponent() {
+
+ }
+
+ public void addListener(HotSwapVetoableListener listener) {
+ myListeners.add(listener);
+ }
+
+ public void removeListener(HotSwapVetoableListener listener) {
+ myListeners.remove(listener);
+ }
+
+ private boolean shouldDisplayHangWarning(DebuggerSettings settings, List<DebuggerSession> sessions) {
+ if (!settings.HOTSWAP_HANG_WARNING_ENABLED) {
+ return false;
+ }
+ // todo: return false if yourkit agent is inactive
+ for (DebuggerSession session : sessions) {
+ if (session.isPaused()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void hotSwapSessions(final List<DebuggerSession> sessions, @Nullable final Map<String, List<String>> generatedPaths) {
+ final boolean shouldAskBeforeHotswap = myAskBeforeHotswap;
+ myAskBeforeHotswap = true;
+
+ // need this because search with PSI is perormed during hotswap
+ PsiDocumentManager.getInstance(myProject).commitAllDocuments();
+
+ final DebuggerSettings settings = DebuggerSettings.getInstance();
+ final String runHotswap = settings.RUN_HOTSWAP_AFTER_COMPILE;
+ final boolean shouldDisplayHangWarning = shouldDisplayHangWarning(settings, sessions);
+
+ if (shouldAskBeforeHotswap && DebuggerSettings.RUN_HOTSWAP_NEVER.equals(runHotswap)) {
+ return;
+ }
+
+ final boolean isOutOfProcessMode = CompilerWorkspaceConfiguration.getInstance(myProject).useOutOfProcessBuild();
+ final boolean shouldPerformScan = !isOutOfProcessMode || generatedPaths == null;
+
+ final HotSwapProgressImpl findClassesProgress;
+ if (shouldPerformScan) {
+ findClassesProgress = new HotSwapProgressImpl(myProject);
+ }
+ else {
+ boolean createProgress = false;
+ for (DebuggerSession session : sessions) {
+ if (session.isModifiedClassesScanRequired()) {
+ createProgress = true;
+ break;
+ }
+ }
+ findClassesProgress = createProgress ? new HotSwapProgressImpl(myProject) : null;
+ }
+
+ ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
+ public void run() {
+ final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses;
+ if (shouldPerformScan) {
+ modifiedClasses = scanForModifiedClassesWithProgress(sessions, findClassesProgress, !isOutOfProcessMode);
+ }
+ else {
+ final List<DebuggerSession> toScan = new ArrayList<DebuggerSession>();
+ final List<DebuggerSession> toUseGenerated = new ArrayList<DebuggerSession>();
+ for (DebuggerSession session : sessions) {
+ (session.isModifiedClassesScanRequired() ? toScan : toUseGenerated).add(session);
+ session.setModifiedClassesScanRequired(false);
+ }
+ modifiedClasses = new HashMap<DebuggerSession, Map<String, HotSwapFile>>();
+ if (!toUseGenerated.isEmpty()) {
+ modifiedClasses.putAll(HotSwapManager.findModifiedClasses(toUseGenerated, generatedPaths));
+ }
+ if (!toScan.isEmpty()) {
+ modifiedClasses.putAll(scanForModifiedClassesWithProgress(toScan, findClassesProgress, !isOutOfProcessMode));
+ }
+ }
+
+ final Application application = ApplicationManager.getApplication();
+ if (modifiedClasses.isEmpty()) {
+ final String message = DebuggerBundle.message("status.hotswap.uptodate");
+ HotSwapProgressImpl.NOTIFICATION_GROUP.createNotification(message, NotificationType.INFORMATION).notify(myProject);
+ return;
+ }
+
+ application.invokeLater(new Runnable() {
+ public void run() {
+ if (shouldAskBeforeHotswap && !DebuggerSettings.RUN_HOTSWAP_ALWAYS.equals(runHotswap)) {
+ final RunHotswapDialog dialog = new RunHotswapDialog(myProject, sessions, shouldDisplayHangWarning);
+ dialog.show();
+ if (!dialog.isOK()) {
+ for (DebuggerSession session : modifiedClasses.keySet()) {
+ session.setModifiedClassesScanRequired(true);
+ }
+ return;
+ }
+ final Set<DebuggerSession> toReload = new HashSet<DebuggerSession>(dialog.getSessionsToReload());
+ for (DebuggerSession session : modifiedClasses.keySet()) {
+ if (!toReload.contains(session)) {
+ session.setModifiedClassesScanRequired(true);
+ }
+ }
+ modifiedClasses.keySet().retainAll(toReload);
+ }
+ else {
+ if (shouldDisplayHangWarning) {
+ final int answer = Messages.showCheckboxMessageDialog(
+ DebuggerBundle.message("hotswap.dialog.hang.warning"),
+ DebuggerBundle.message("hotswap.dialog.title"),
+ new String[]{"Perform &Reload Classes", "&Skip Reload Classes"},
+ CommonBundle.message("dialog.options.do.not.show"),
+ false, 1, 1, Messages.getWarningIcon(),
+ new PairFunction<Integer, JCheckBox, Integer>() {
+ @Override
+ public Integer fun(Integer exitCode, JCheckBox cb) {
+ settings.HOTSWAP_HANG_WARNING_ENABLED = !cb.isSelected();
+ return exitCode == DialogWrapper.OK_EXIT_CODE ? exitCode : DialogWrapper.CANCEL_EXIT_CODE;
+ }
+ }
+ );
+ if (answer == DialogWrapper.CANCEL_EXIT_CODE) {
+ for (DebuggerSession session : modifiedClasses.keySet()) {
+ session.setModifiedClassesScanRequired(true);
+ }
+ return;
+ }
+ }
+ }
+
+ if (!modifiedClasses.isEmpty()) {
+ final HotSwapProgressImpl progress = new HotSwapProgressImpl(myProject);
+ application.executeOnPooledThread(new Runnable() {
+ public void run() {
+ reloadModifiedClasses(modifiedClasses, progress);
+ }
+ });
+ }
+ }
+ }, ModalityState.NON_MODAL);
+ }
+ });
+ }
+
+ private static Map<DebuggerSession, Map<String, HotSwapFile>> scanForModifiedClassesWithProgress(final List<DebuggerSession> sessions,
+ final HotSwapProgressImpl progress,
+ final boolean scanWithVFS) {
+ final Ref<Map<DebuggerSession, Map<String, HotSwapFile>>> result = Ref.create(null);
+ ProgressManager.getInstance().runProcess(new Runnable() {
+ public void run() {
+ try {
+ result.set(HotSwapManager.scanForModifiedClasses(sessions, progress, scanWithVFS));
+ }
+ finally {
+ progress.finished();
+ }
+ }
+ }, progress.getProgressIndicator());
+ return result.get();
+ }
+
+ private static void reloadModifiedClasses(final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses,
+ final HotSwapProgressImpl progress) {
+ ProgressManager.getInstance().runProcess(new Runnable() {
+ public void run() {
+ HotSwapManager.reloadModifiedClasses(modifiedClasses, progress);
+ progress.finished();
+ }
+ }, progress.getProgressIndicator());
+ }
+
+ public void reloadChangedClasses(final DebuggerSession session, boolean compileBeforeHotswap) {
+ dontAskHotswapAfterThisCompilation();
+ if (compileBeforeHotswap) {
+ CompilerManager.getInstance(session.getProject()).make(null);
+ }
+ else {
+ if (session.isAttached()) {
+ hotSwapSessions(Collections.singletonList(session), null);
+ }
+ }
+ }
+
+ public void dontPerformHotswapAfterThisCompilation() {
+ myPerformHotswapAfterThisCompilation = false;
+ }
+
+ public void dontAskHotswapAfterThisCompilation() {
+ myAskBeforeHotswap = false;
+ }
+
+ private class MyCompilationStatusListener implements CompilationStatusListener {
+
+ private final AtomicReference<Map<String, List<String>>>
+ myGeneratedPaths = new AtomicReference<Map<String, List<String>>>(new HashMap<String, List<String>>());
+
+ public void fileGenerated(String outputRoot, String relativePath) {
+ if (StringUtil.endsWith(relativePath, ".class")) {
+ // collect only classes
+ final Map<String, List<String>> map = myGeneratedPaths.get();
+ List<String> paths = map.get(outputRoot);
+ if (paths == null) {
+ paths = new ArrayList<String>();
+ map.put(outputRoot, paths);
+ }
+ paths.add(relativePath);
+ }
+ }
+
+ public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
+ final Map<String, List<String>> generated = myGeneratedPaths.getAndSet(new HashMap<String, List<String>>());
+ if (myProject.isDisposed()) {
+ return;
+ }
+
+ if (errors == 0 && !aborted && myPerformHotswapAfterThisCompilation) {
+ for (HotSwapVetoableListener listener : myListeners) {
+ if (!listener.shouldHotSwap(compileContext)) {
+ return;
+ }
+ }
+
+ final List<DebuggerSession> sessions = new ArrayList<DebuggerSession>();
+ Collection<DebuggerSession> debuggerSessions = DebuggerManagerEx.getInstanceEx(myProject).getSessions();
+ for (final DebuggerSession debuggerSession : debuggerSessions) {
+ if (debuggerSession.isAttached() && debuggerSession.getProcess().canRedefineClasses()) {
+ sessions.add(debuggerSession);
+ }
+ }
+ if (!sessions.isEmpty()) {
+ hotSwapSessions(sessions, generated);
+ }
+ }
+ myPerformHotswapAfterThisCompilation = true;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapVetoableListener.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapVetoableListener.java
new file mode 100644
index 0000000..105f5d4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapVetoableListener.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package com.intellij.debugger.ui;
+
+import com.intellij.openapi.compiler.CompileContext;
+
+/**
+ * @author nik
+ */
+public interface HotSwapVetoableListener {
+
+ boolean shouldHotSwap(CompileContext finishedCompilationContext);
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/InstanceFilterEditor.java b/java/debugger/impl/src/com/intellij/debugger/ui/InstanceFilterEditor.java
new file mode 100644
index 0000000..7970a9c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/InstanceFilterEditor.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.ui.classFilter.ClassFilterEditor;
+import com.intellij.util.IconUtil;
+
+import javax.swing.*;
+
+/**
+ * User: lex
+ * Date: Aug 29, 2003
+ * Time: 2:38:30 PM
+ */
+public class InstanceFilterEditor extends ClassFilterEditor {
+ public InstanceFilterEditor(Project project) {
+ super(project);
+ }
+
+ protected void addClassFilter() {
+ String idString = Messages.showInputDialog(myProject, DebuggerBundle.message("add.instance.filter.dialog.prompt"), DebuggerBundle.message("add.instance.filter.dialog.title"), Messages.getQuestionIcon());
+ if (idString != null) {
+ ClassFilter filter = createFilter(idString);
+ if(filter != null){
+ myTableModel.addRow(filter);
+ int row = myTableModel.getRowCount() - 1;
+ myTable.getSelectionModel().setSelectionInterval(row, row);
+ myTable.scrollRectToVisible(myTable.getCellRect(row, 0, true));
+
+ }
+ myTable.requestFocus();
+ }
+ }
+
+ protected String getAddButtonText() {
+ return DebuggerBundle.message("button.add");
+ }
+
+ @Override
+ protected Icon getAddButtonIcon() {
+ return IconUtil.getAddIcon();
+ }
+
+ @Override
+ protected boolean addPatternButtonVisible() {
+ return false;
+ }
+
+ protected ClassFilter createFilter(String pattern) {
+ try {
+ Long.parseLong(pattern);
+ return super.createFilter(pattern);
+ } catch (NumberFormatException e) {
+ Messages.showMessageDialog(this, DebuggerBundle.message("add.instance.filter.dialog.error.numeric.value.expected"), DebuggerBundle.message("add.instance.filter.dialog.title"), Messages.getErrorIcon());
+ return null;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java
new file mode 100644
index 0000000..9ae2a3d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.actions.*;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.settings.*;
+import com.intellij.debugger.ui.breakpoints.*;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.util.Key;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.xdebugger.AbstractDebuggerSession;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
+import com.intellij.xdebugger.impl.DebuggerSupport;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import com.intellij.xdebugger.impl.actions.DebuggerToggleActionHandler;
+import com.intellij.xdebugger.impl.actions.EditBreakpointActionHandler;
+import com.intellij.xdebugger.impl.actions.MarkObjectActionHandler;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointPanelProvider;
+import com.intellij.xdebugger.impl.evaluate.quick.common.QuickEvaluateHandler;
+import com.intellij.xdebugger.impl.settings.DebuggerSettingsPanelProvider;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JavaDebuggerSupport extends DebuggerSupport {
+ private final JavaBreakpointPanelProvider myBreakpointPanelProvider = new JavaBreakpointPanelProvider();
+ private final StepOverActionHandler myStepOverActionHandler = new StepOverActionHandler();
+ private final StepIntoActionHandler myStepIntoActionHandler = new StepIntoActionHandler();
+ private final StepOutActionHandler myStepOutActionHandler = new StepOutActionHandler();
+ private final ForceStepOverActionHandler myForceStepOverActionHandler = new ForceStepOverActionHandler();
+ private final ForceStepIntoActionHandler myForceStepIntoActionHandler = new ForceStepIntoActionHandler();
+ private final RunToCursorActionHandler myRunToCursorActionHandler = new RunToCursorActionHandler();
+ private final ForceRunToCursorActionHandler myForceRunToCursorActionHandler = new ForceRunToCursorActionHandler();
+ private final ResumeActionHandler myResumeActionHandler = new ResumeActionHandler();
+ private final PauseActionHandler myPauseActionHandler = new PauseActionHandler();
+ private final ToggleLineBreakpointActionHandler myToggleLineBreakpointActionHandler = new ToggleLineBreakpointActionHandler();
+ private final ShowExecutionPointActionHandler myShowExecutionPointActionHandler = new ShowExecutionPointActionHandler();
+ private final EvaluateActionHandler myEvaluateActionHandler = new EvaluateActionHandler();
+ private final QuickEvaluateActionHandler myQuickEvaluateHandler = new QuickEvaluateActionHandler();
+ private final JavaDebuggerSettingsPanelProvider myDebuggerSettingsPanelProvider = new JavaDebuggerSettingsPanelProvider();
+ private final MuteBreakpointsActionHandler myMuteBreakpointsHandler = new MuteBreakpointsActionHandler();
+ private final DebuggerActionHandler mySmartStepIntoHandler = new JvmSmartStepIntoActionHandler();
+ private final DebuggerActionHandler myAddToWatchedActionHandler = new AddToWatchActionHandler();
+ private final JavaMarkObjectActionHandler myMarkObjectActionHandler = new JavaMarkObjectActionHandler();
+ private final JavaEditBreakpointActionHandler myEditBreakpointActionHandler = new JavaEditBreakpointActionHandler();
+
+ @NotNull
+ public BreakpointPanelProvider<?> getBreakpointPanelProvider() {
+ return myBreakpointPanelProvider;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getStepOverHandler() {
+ return myStepOverActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getStepIntoHandler() {
+ return myStepIntoActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getSmartStepIntoHandler() {
+ return mySmartStepIntoHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getStepOutHandler() {
+ return myStepOutActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getForceStepOverHandler() {
+ return myForceStepOverActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getForceStepIntoHandler() {
+ return myForceStepIntoActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getRunToCursorHandler() {
+ return myRunToCursorActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getForceRunToCursorHandler() {
+ return myForceRunToCursorActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getResumeActionHandler() {
+ return myResumeActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getPauseHandler() {
+ return myPauseActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getToggleLineBreakpointHandler() {
+ return myToggleLineBreakpointActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getShowExecutionPointHandler() {
+ return myShowExecutionPointActionHandler;
+ }
+
+ @NotNull
+ public DebuggerActionHandler getEvaluateHandler() {
+ return myEvaluateActionHandler;
+ }
+
+ @NotNull
+ public QuickEvaluateHandler getQuickEvaluateHandler() {
+ return myQuickEvaluateHandler;
+ }
+
+ @NotNull
+ @Override
+ public DebuggerActionHandler getAddToWatchesActionHandler() {
+ return myAddToWatchedActionHandler;
+ }
+
+ @NotNull
+ public DebuggerToggleActionHandler getMuteBreakpointsHandler() {
+ return myMuteBreakpointsHandler;
+ }
+
+ @NotNull
+ @Override
+ public MarkObjectActionHandler getMarkObjectHandler() {
+ return myMarkObjectActionHandler;
+ }
+
+ @Override
+ public AbstractDebuggerSession getCurrentSession(@NotNull Project project) {
+ final DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(project)).getContext();
+ return context != null ? context.getDebuggerSession() : null;
+ }
+
+ @NotNull
+ @Override
+ public EditBreakpointActionHandler getEditBreakpointAction() {
+ return myEditBreakpointActionHandler;
+ }
+
+ @NotNull
+ public DebuggerSettingsPanelProvider getSettingsPanelProvider() {
+ return myDebuggerSettingsPanelProvider;
+ }
+
+ private static class JavaBreakpointPanelProvider extends BreakpointPanelProvider<Breakpoint> {
+ private List<MyBreakpointManagerListener> myListeners = ContainerUtil.createEmptyCOWList();
+
+ @Override
+ public AnAction[] getAddBreakpointActions(@NotNull Project project) {
+ List<AnAction> result = new ArrayList<AnAction>();
+ BreakpointFactory[] breakpointFactories = BreakpointFactory.getBreakpointFactories();
+ for (BreakpointFactory breakpointFactory : breakpointFactories) {
+ result.add(new AddJavaBreakpointAction(breakpointFactory));
+ }
+ return result.toArray(new AnAction[result.size()]);
+ }
+
+ @Override
+ public void createBreakpointsGroupingRules(Collection<XBreakpointGroupingRule> rules) {
+ rules.add(new XBreakpointGroupingByCategoryRule());
+ rules.add(new XBreakpointGroupingByPackageRule());
+ rules.add(new XBreakpointGroupingByClassRule());
+ }
+
+ @Override
+ public void addListener(final BreakpointsListener listener, Project project) {
+ BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getCurrentProject()).getBreakpointManager();
+ final MyBreakpointManagerListener listener1 = new MyBreakpointManagerListener(listener, breakpointManager);
+ breakpointManager.addBreakpointManagerListener(listener1);
+ myListeners.add(listener1);
+ }
+
+ @Override
+ public void removeListener(BreakpointsListener listener) {
+ for (MyBreakpointManagerListener managerListener : myListeners) {
+ if (managerListener.myListener == listener) {
+ BreakpointManager manager = managerListener.myBreakpointManager;
+ manager.removeBreakpointManagerListener(managerListener);
+ myListeners.remove(managerListener);
+ break;
+ }
+ }
+ }
+
+ public int getPriority() {
+ return 1;
+ }
+
+ public Breakpoint findBreakpoint(@NotNull final Project project, @NotNull final Document document, final int offset) {
+ return DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().findBreakpoint(document, offset, null);
+ }
+
+ @Override
+ public GutterIconRenderer getBreakpointGutterIconRenderer(Object breakpoint) {
+ if (breakpoint instanceof BreakpointWithHighlighter) {
+ final RangeHighlighter highlighter = ((BreakpointWithHighlighter)breakpoint).getHighlighter();
+ if (highlighter != null) {
+ return highlighter.getGutterIconRenderer();
+ }
+ }
+ return null;
+ }
+
+ public void onDialogClosed(final Project project) {
+ DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().updateAllRequests();
+ }
+
+ @Override
+ public void provideBreakpointItems(Project project, Collection<BreakpointItem> items) {
+ for (BreakpointFactory breakpointFactory : BreakpointFactory.getBreakpointFactories()) {
+ Key<? extends Breakpoint> category = breakpointFactory.getBreakpointCategory();
+ Breakpoint[] breakpoints = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().getBreakpoints(category);
+ for (Breakpoint breakpoint : breakpoints) {
+ items.add(breakpointFactory.createBreakpointItem(breakpoint));
+ }
+ }
+ }
+
+ private static class AddJavaBreakpointAction extends AnAction {
+ private BreakpointFactory myBreakpointFactory;
+
+ public AddJavaBreakpointAction(BreakpointFactory breakpointFactory) {
+ myBreakpointFactory = breakpointFactory;
+ Presentation p = getTemplatePresentation();
+ p.setIcon(myBreakpointFactory.getIcon());
+ p.setText(breakpointFactory.getDisplayName());
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setVisible(myBreakpointFactory.canAddBreakpoints());
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myBreakpointFactory.addBreakpoint(getEventProject(e));
+ }
+ }
+
+ private static class MyBreakpointManagerListener implements BreakpointManagerListener {
+
+ private final BreakpointsListener myListener;
+ public BreakpointManager myBreakpointManager;
+
+
+ public MyBreakpointManagerListener(BreakpointsListener listener, BreakpointManager breakpointManager) {
+ myListener = listener;
+ myBreakpointManager = breakpointManager;
+ }
+
+ @Override
+ public void breakpointsChanged() {
+ myListener.breakpointsChanged();
+ }
+ }
+ }
+
+ public static class JavaDebuggerSettingsPanelProvider extends DebuggerSettingsPanelProvider {
+ public int getPriority() {
+ return 1;
+ }
+
+ @Override
+ public Configurable getRootConfigurable() {
+ return new DebuggerLaunchingConfigurable();
+ }
+
+ public Collection<? extends Configurable> getConfigurables() {
+ final ArrayList<Configurable> configurables = new ArrayList<Configurable>();
+ configurables.add(new DebuggerDataViewsConfigurable(null));
+ configurables.add(new DebuggerSteppingConfigurable());
+ configurables.add(new UserRenderersConfigurable(null));
+ configurables.add(new DebuggerHotswapConfigurable());
+ return configurables;
+ }
+
+ public void apply() {
+ NodeRendererSettings.getInstance().fireRenderersChanged();
+ }
+ }
+
+ public static Project getCurrentProject() {
+ //todo[nik] improve
+ Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
+ if (project != null) {
+ return project;
+ }
+ return ProjectManager.getInstance().getDefaultProject();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/PositionHighlighter.java b/java/debugger/impl/src/com/intellij/debugger/ui/PositionHighlighter.java
new file mode 100644
index 0000000..1339e9c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/PositionHighlighter.java
@@ -0,0 +1,443 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.codeInsight.daemon.impl.DaemonListeners;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebugProcessEvents;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.*;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.breakpoints.*;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.ex.DocumentEx;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.xdebugger.impl.actions.ViewBreakpointsAction;
+import com.intellij.xdebugger.ui.DebuggerColors;
+import com.sun.jdi.event.Event;
+import com.sun.jdi.event.LocatableEvent;
+import com.sun.jdi.event.MethodEntryEvent;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jul 9, 2003
+ * Time: 6:24:35 PM
+ * To change this template use Options | File Templates.
+ */
+public class PositionHighlighter {
+ private static final Key<Boolean> HIGHLIGHTER_USERDATA_KEY = new Key<Boolean>("HIGHLIGHTER_USERDATA_KEY");
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.PositionHighlighter");
+ private final Project myProject;
+ private DebuggerContextImpl myContext = DebuggerContextImpl.EMPTY_CONTEXT;
+ private SelectionDescription mySelectionDescription = null;
+ private ExecutionPointDescription myExecutionPointDescription = null;
+
+ public PositionHighlighter(Project project, DebuggerStateManager stateManager) {
+ myProject = project;
+
+ stateManager.addListener(new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ myContext = newContext;
+ if (event != DebuggerSession.EVENT_REFRESH_VIEWS_ONLY && event != DebuggerSession.EVENT_THREADS_REFRESH) {
+ refresh();
+ }
+ }
+ });
+ }
+
+ private void showLocationInEditor() {
+ myContext.getDebugProcess().getManagerThread().schedule(new ShowLocationCommand(myContext));
+ }
+
+ private void refresh() {
+ clearSelections();
+ final DebuggerSession session = myContext.getDebuggerSession();
+ if(session != null) {
+ switch(session.getState()) {
+ case DebuggerSession.STATE_PAUSED:
+ if(myContext.getFrameProxy() != null) {
+ showLocationInEditor();
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ protected static class ExecutionPointDescription extends SelectionDescription {
+ private RangeHighlighter myHighlighter;
+ private final int myLineIndex;
+
+ protected ExecutionPointDescription(Editor editor, int lineIndex) {
+ super(editor);
+ myLineIndex = lineIndex;
+ }
+
+ public void select() {
+ if(myIsActive) return;
+ myIsActive = true;
+ EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
+ myHighlighter = myEditor.getMarkupModel().addLineHighlighter(
+ myLineIndex,
+ DebuggerColors.EXECUTION_LINE_HIGHLIGHTERLAYER,
+ scheme.getAttributes(DebuggerColors.EXECUTIONPOINT_ATTRIBUTES)
+ );
+ adjustCounter(myEditor, 1);
+ myHighlighter.setErrorStripeTooltip(DebuggerBundle.message("position.highlighter.stripe.tooltip"));
+ myHighlighter.putUserData(HIGHLIGHTER_USERDATA_KEY, Boolean.TRUE);
+ }
+
+ private static void adjustCounter(@NotNull Editor editor, int increment) {
+ JComponent component = editor.getComponent();
+ Object o = component.getClientProperty(DaemonListeners.IGNORE_MOUSE_TRACKING);
+ Integer value = ((o instanceof Integer) ? (Integer)o : 0) + increment;
+ component.putClientProperty(DaemonListeners.IGNORE_MOUSE_TRACKING, value > 0 ? value : null);
+ }
+
+ public void remove() {
+ if(!myIsActive) return;
+ myIsActive = false;
+ adjustCounter(myEditor, -1);
+ if (myHighlighter != null) {
+ myHighlighter.dispose();
+ myHighlighter = null;
+ }
+ }
+
+ public RangeHighlighter getHighlighter() {
+ return myHighlighter;
+ }
+ }
+
+ protected abstract static class SelectionDescription {
+ protected Editor myEditor;
+ protected boolean myIsActive;
+
+ public SelectionDescription(Editor editor) {
+ myEditor = editor;
+ }
+
+ public abstract void select();
+ public abstract void remove();
+
+ public static ExecutionPointDescription createExecutionPoint(final Editor editor,
+ final int lineIndex) {
+ return new ExecutionPointDescription(editor, lineIndex);
+ }
+
+ public static SelectionDescription createSelection(final Editor editor, final int lineIndex) {
+ return new SelectionDescription(editor) {
+ public void select() {
+ if(myIsActive) return;
+ myIsActive = true;
+ DocumentEx doc = (DocumentEx)editor.getDocument();
+ editor.getSelectionModel().setSelection(
+ doc.getLineStartOffset(lineIndex),
+ doc.getLineEndOffset(lineIndex) + doc.getLineSeparatorLength(lineIndex)
+ );
+ }
+
+ public void remove() {
+ if(!myIsActive) return;
+ myIsActive = false;
+ myEditor.getSelectionModel().removeSelection();
+ }
+ };
+ }
+ }
+
+ private void showSelection(SourcePosition position) {
+ Editor editor = getEditor(position);
+ if(editor == null) {
+ return;
+ }
+ if (mySelectionDescription != null) {
+ mySelectionDescription.remove();
+ }
+ mySelectionDescription = SelectionDescription.createSelection(editor, position.getLine());
+ mySelectionDescription.select();
+ }
+
+ private void showExecutionPoint(final SourcePosition position, List<Pair<Breakpoint, Event>> events) {
+ if (myExecutionPointDescription != null) {
+ myExecutionPointDescription.remove();
+ }
+ int lineIndex = position.getLine();
+ Editor editor = getEditor(position);
+ if(editor == null) {
+ return;
+ }
+ myExecutionPointDescription = SelectionDescription.createExecutionPoint(editor, lineIndex);
+ myExecutionPointDescription.select();
+
+ RangeHighlighter highlighter = myExecutionPointDescription.getHighlighter();
+
+ if(highlighter != null) {
+ final List<Pair<Breakpoint, Event>> eventsOutOfLine = new ArrayList<Pair<Breakpoint, Event>>();
+
+ for (final Pair<Breakpoint, Event> eventDescriptor : events) {
+ final Breakpoint breakpoint = eventDescriptor.getFirst();
+ // filter breakpoints that do not match the event
+ if (breakpoint instanceof MethodBreakpoint) {
+ try {
+ if (!((MethodBreakpoint)breakpoint).matchesEvent((LocatableEvent)eventDescriptor.getSecond(), myContext.getDebugProcess())) {
+ continue;
+ }
+ }
+ catch (EvaluateException ignored) {
+ }
+ }
+ else if (breakpoint instanceof WildcardMethodBreakpoint) {
+ if (!((WildcardMethodBreakpoint)breakpoint).matchesEvent((LocatableEvent)eventDescriptor.getSecond())) {
+ continue;
+ }
+ }
+
+ if (breakpoint instanceof BreakpointWithHighlighter) {
+ if (((BreakpointWithHighlighter)breakpoint).isVisible() && breakpoint.isValid()) {
+ breakpoint.reload();
+ final SourcePosition sourcePosition = ((BreakpointWithHighlighter)breakpoint).getSourcePosition();
+ if (sourcePosition == null || sourcePosition.getLine() != lineIndex) {
+ eventsOutOfLine.add(eventDescriptor);
+ }
+ }
+ }
+ else {
+ eventsOutOfLine.add(eventDescriptor);
+ }
+ }
+
+ if(!eventsOutOfLine.isEmpty()) {
+ highlighter.setGutterIconRenderer(new MyGutterIconRenderer(eventsOutOfLine));
+ }
+ }
+ }
+
+ private Editor getEditor(SourcePosition position) {
+ final PsiFile psiFile = position.getFile();
+ Document doc = PsiDocumentManager.getInstance(myProject).getDocument(psiFile);
+ if (!psiFile.isValid()) {
+ return null;
+ }
+ final int lineIndex = position.getLine();
+ if (lineIndex < 0 || lineIndex > doc.getLineCount()) {
+ //LOG.assertTrue(false, "Incorrect lineIndex " + lineIndex + " in file " + psiFile.getName());
+ return null;
+ }
+ return position.openEditor(false);
+ }
+
+ private void clearSelections() {
+ if (mySelectionDescription != null || myExecutionPointDescription != null) {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ if (mySelectionDescription != null) {
+ mySelectionDescription.remove();
+ mySelectionDescription = null;
+ }
+ if (myExecutionPointDescription != null) {
+ myExecutionPointDescription.remove();
+ myExecutionPointDescription = null;
+ }
+ }
+ });
+ }
+ }
+
+
+ public void updateContextPointDescription() {
+ if(myContext.getDebuggerSession() == null) return;
+
+ showLocationInEditor();
+ }
+
+ private class ShowLocationCommand extends DebuggerContextCommandImpl {
+ private final DebuggerContextImpl myContext;
+
+ public ShowLocationCommand(DebuggerContextImpl context) {
+ super(context);
+ myContext = context;
+ }
+
+ public void threadAction() {
+ final SourcePosition contextPosition = myContext.getSourcePosition();
+ if (contextPosition == null) {
+ return;
+ }
+
+ boolean isExecutionPoint = false;
+ try {
+ StackFrameProxyImpl frameProxy = myContext.getFrameProxy();
+ final ThreadReferenceProxyImpl thread = getSuspendContext().getThread();
+ isExecutionPoint = thread != null && frameProxy != null && frameProxy.equals(thread.frame(0));
+ }
+ catch(Throwable th) {
+ LOG.debug(th);
+ }
+
+ final List<Pair<Breakpoint, Event>> events = DebuggerUtilsEx.getEventDescriptors(getSuspendContext());
+
+ final SourcePosition position = ApplicationManager.getApplication().runReadAction(new Computable<SourcePosition>() {
+ public SourcePosition compute() {
+ Document document = PsiDocumentManager.getInstance(myProject).getDocument(contextPosition.getFile());
+ if(document != null) {
+ if(contextPosition.getLine() < 0 || contextPosition.getLine() >= document.getLineCount()) {
+ return SourcePosition.createFromLine(contextPosition.getFile(), 0);
+ }
+ }
+ return contextPosition;
+ }
+ });
+
+ if(isExecutionPoint) {
+ DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
+ public void run() {
+ final SourcePosition highlightPosition = getHighlightPosition(events, position);
+ showExecutionPoint(highlightPosition, events);
+ }
+ });
+ }
+ else {
+ DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
+ public void run() {
+ showSelection(position);
+ }
+ });
+ }
+ }
+
+ private SourcePosition getHighlightPosition(final List<Pair<Breakpoint, Event>> events, SourcePosition position) {
+ for (Iterator<Pair<Breakpoint, Event>> iterator = events.iterator(); iterator.hasNext();) {
+ final Pair<Breakpoint, Event> eventDescriptor = iterator.next();
+ final Breakpoint breakpoint = eventDescriptor.getFirst();
+ if(breakpoint instanceof LineBreakpoint) {
+ breakpoint.reload();
+ final SourcePosition breakPosition = ((BreakpointWithHighlighter)breakpoint).getSourcePosition();
+ if(breakPosition != null && breakPosition.getLine() != position.getLine()) {
+ position = SourcePosition.createFromLine(position.getFile(), breakPosition.getLine());
+ }
+ }
+ else if(breakpoint instanceof MethodBreakpoint) {
+ final MethodBreakpoint methodBreakpoint = (MethodBreakpoint)breakpoint;
+ methodBreakpoint.reload();
+ final SourcePosition breakPosition = methodBreakpoint.getSourcePosition();
+ final LocatableEvent event = (LocatableEvent)eventDescriptor.getSecond();
+ if(breakPosition != null && breakPosition.getFile().equals(position.getFile()) && breakPosition.getLine() != position.getLine() && event instanceof MethodEntryEvent) {
+ try {
+ if (methodBreakpoint.matchesEvent(event, myContext.getDebugProcess())) {
+ position = SourcePosition.createFromLine(position.getFile(), breakPosition.getLine());
+ }
+ }
+ catch (EvaluateException ignored) {
+ }
+ }
+ }
+ }
+ return position;
+ }
+ }
+
+ private class MyGutterIconRenderer extends GutterIconRenderer {
+ private final List<Pair<Breakpoint, Event>> myEventsOutOfLine;
+
+ public MyGutterIconRenderer(List<Pair<Breakpoint, Event>> eventsOutOfLine) {
+ myEventsOutOfLine = eventsOutOfLine;
+ }
+
+ @NotNull
+ public Icon getIcon() {
+ return myEventsOutOfLine.get(0).getFirst().getIcon();
+ }
+
+ public String getTooltipText() {
+ DebugProcessImpl debugProcess = myContext.getDebugProcess();
+ if (debugProcess == null) {
+ return null;
+ }
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ //noinspection HardCodedStringLiteral
+ buf.append("<html><body>");
+ for (Iterator<Pair<Breakpoint, Event>> iterator = myEventsOutOfLine.iterator(); iterator.hasNext();) {
+ Pair<Breakpoint, Event> eventDescriptor = iterator.next();
+ buf.append(((DebugProcessEvents)debugProcess).getEventText(eventDescriptor));
+ if(iterator.hasNext()) {
+ //noinspection HardCodedStringLiteral
+ buf.append("<br>");
+ }
+ }
+ //noinspection HardCodedStringLiteral
+ buf.append("</body></html>");
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ public ActionGroup getPopupMenuActions() {
+ DefaultActionGroup group = new DefaultActionGroup();
+ for (Pair<Breakpoint, Event> eventDescriptor : myEventsOutOfLine) {
+ Breakpoint breakpoint = eventDescriptor.getFirst();
+ AnAction viewBreakpointsAction = new ViewBreakpointsAction(breakpoint.getDisplayName(), breakpoint);
+ group.add(viewBreakpointsAction);
+ }
+
+ return group;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof MyGutterIconRenderer &&
+ Comparing.equal(getTooltipText(), ((MyGutterIconRenderer)obj).getTooltipText()) &&
+ Comparing.equal(getIcon(), ((MyGutterIconRenderer)obj).getIcon());
+ }
+
+ @Override
+ public int hashCode() {
+ return getIcon().hashCode();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/RunHotswapDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/RunHotswapDialog.java
new file mode 100644
index 0000000..36a5351
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/RunHotswapDialog.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.CommonBundle;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.ui.OptionsDialog;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Oct 6, 2003
+ * Time: 5:58:17 PM
+ */
+
+
+
+public class RunHotswapDialog extends OptionsDialog {
+ private final JPanel myPanel;
+ private final ElementsChooser<SessionItem> myElementsChooser;
+ private final boolean myDisplayHangWarning;
+
+ public RunHotswapDialog(Project project, List<DebuggerSession> sessions, boolean displayHangWarning) {
+ super(project);
+ myDisplayHangWarning = displayHangWarning;
+ myPanel = new JPanel(new BorderLayout());
+ final List<SessionItem> items = new ArrayList<SessionItem>(sessions.size());
+ for (DebuggerSession session : sessions) {
+ items.add(new SessionItem(session));
+ }
+ Collections.sort(items, new Comparator<SessionItem>() {
+ public int compare(SessionItem debuggerSession, SessionItem debuggerSession1) {
+ return debuggerSession.getSession().getSessionName().compareTo(debuggerSession1.getSession().getSessionName());
+ }
+ });
+ myElementsChooser = new ElementsChooser<SessionItem>(items, true);
+ myPanel.setBorder(IdeBorderFactory.createEmptyBorder(10, 0, 5, 0));
+ //myElementsChooser.setBorder(IdeBorderFactory.createEmptyBorder(5, 0, 0, 0));
+ if (sessions.size() > 0) {
+ myElementsChooser.selectElements(items.subList(0, 1));
+ }
+ myPanel.add(myElementsChooser, BorderLayout.CENTER);
+ //myPanel.add(new JLabel("Choose debug sessions to reload classes:"), BorderLayout.NORTH);
+ if(sessions.size() == 1) {
+ setTitle(DebuggerBundle.message("hotswap.dialog.title.with.session", sessions.get(0).getSessionName()));
+ myPanel.setVisible(false);
+ }
+ else {
+ setTitle(DebuggerBundle.message("hotswap.dialog.title"));
+ }
+ setButtonsAlignment(SwingUtilities.CENTER);
+ this.init();
+ }
+
+ protected boolean isToBeShown() {
+ return DebuggerSettings.RUN_HOTSWAP_ASK.equals(DebuggerSettings.getInstance().RUN_HOTSWAP_AFTER_COMPILE);
+ }
+
+ protected void setToBeShown(boolean value, boolean onOk) {
+ if (value) {
+ DebuggerSettings.getInstance().RUN_HOTSWAP_AFTER_COMPILE = DebuggerSettings.RUN_HOTSWAP_ASK;
+ }
+ else {
+ if (onOk) {
+ DebuggerSettings.getInstance().RUN_HOTSWAP_AFTER_COMPILE = DebuggerSettings.RUN_HOTSWAP_ALWAYS;
+ }
+ else {
+ DebuggerSettings.getInstance().RUN_HOTSWAP_AFTER_COMPILE = DebuggerSettings.RUN_HOTSWAP_NEVER;
+ }
+ }
+ }
+
+ protected boolean shouldSaveOptionsOnCancel() {
+ return true;
+ }
+
+ protected Action[] createActions(){
+ setOKButtonText(CommonBundle.getYesButtonText());
+ setCancelButtonText(CommonBundle.getNoButtonText());
+ return new Action[]{getOKAction(), getCancelAction()};
+ }
+
+ protected JComponent createNorthPanel() {
+ JLabel label = new JLabel(DebuggerBundle.message("hotswap.dialog.run.prompt"));
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(label, BorderLayout.CENTER);
+ Icon icon = UIUtil.getQuestionIcon();
+ if (icon != null) {
+ label.setIcon(icon);
+ label.setIconTextGap(7);
+ }
+ if (myDisplayHangWarning) {
+ final JLabel warningLabel = new JLabel("WARNING! " + DebuggerBundle.message("hotswap.dialog.hang.warning"));
+ warningLabel.setUI(new MultiLineLabelUI());
+ panel.add(warningLabel, BorderLayout.SOUTH);
+ }
+ return panel;
+ }
+
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+
+ public Collection<DebuggerSession> getSessionsToReload() {
+ final List<SessionItem> markedElements = myElementsChooser.getMarkedElements();
+ final List<DebuggerSession> sessions = new ArrayList<DebuggerSession>(markedElements.size());
+ for (SessionItem item : markedElements) {
+ sessions.add(item.getSession());
+ }
+ return sessions;
+ }
+
+ private static class SessionItem {
+ private final DebuggerSession mySession;
+
+ public SessionItem(DebuggerSession session) {
+ mySession = session;
+ }
+
+ public DebuggerSession getSession() {
+ return mySession;
+ }
+
+ public String toString() {
+ return mySession.getSessionName();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/StatementEvaluationDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/StatementEvaluationDialog.java
new file mode 100644
index 0000000..b8de25a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/StatementEvaluationDialog.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.actions.EvaluateActionHandler;
+import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.event.DocumentAdapter;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.help.HelpManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.util.DimensionService;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiWhiteSpace;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+/**
+ * @author lex
+ */
+public class StatementEvaluationDialog extends EvaluationDialog{
+ private final JPanel myPanel;
+ private final Action mySwitchAction = new SwitchAction();
+ private static final @NonNls String STATEMENT_EDITOR_DIMENSION_KEY = "#com.intellij.debugger.ui.StatementEvaluationDialog.StatementEditor";
+ private static final @NonNls String EVALUATION_PANEL_DIMENSION_KEY = "#com.intellij.debugger.ui.StatementEvaluationDialog.EvaluationPanel";
+
+ public StatementEvaluationDialog(final Project project, TextWithImports text) {
+ super(project, text);
+ setTitle(DebuggerBundle.message("evaluate.statement.dialog.title"));
+ myPanel = new JPanel(new BorderLayout());
+
+ final Splitter splitter = new Splitter(true);
+ splitter.setHonorComponentsMinimumSize(true);
+
+ final JPanel editorPanel = new JPanel(new GridBagLayout());
+
+ final JLabel statementsLabel = new JLabel(DebuggerBundle.message("label.evaluation.dialog.statements"));
+ editorPanel.add(statementsLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0));
+ editorPanel.add(getStatementEditor(), new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 0, 0, 0), 0, 0));
+
+ splitter.setFirstComponent(editorPanel);
+
+ final MyEvaluationPanel evaluationPanel = getEvaluationPanel();
+ final JPanel ep = new JPanel(new BorderLayout());
+ //final JLabel resultLabel = new JLabel(DebuggerBundle.message("label.evaluate.dialog.result"));
+ //ep.add(resultLabel, BorderLayout.NORTH);
+ ep.add(evaluationPanel, BorderLayout.CENTER);
+ splitter.setSecondComponent(ep);
+ final Dimension statementSize = DimensionService.getInstance().getSize(STATEMENT_EDITOR_DIMENSION_KEY, project);
+ final Dimension evaluationSize = DimensionService.getInstance().getSize(EVALUATION_PANEL_DIMENSION_KEY, project);
+ if (statementSize != null && evaluationSize != null) {
+ final float proportion = (float)statementSize.height / (float)(statementSize.height + evaluationSize.height);
+ splitter.setProportion(proportion);
+ }
+ myPanel.add(splitter, BorderLayout.CENTER);
+
+ setDebuggerContext(getDebuggerContext());
+
+ final KeyStroke codeFragment = KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.ALT_MASK);
+ final KeyStroke resultStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.ALT_MASK);
+ final KeyStroke altEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK);
+
+ final JRootPane rootPane = getRootPane();
+ final AnAction toStatementAction = new AnAction() {
+ public void actionPerformed(AnActionEvent e) {
+ getStatementEditor().requestFocus();
+ }
+ };
+ toStatementAction.registerCustomShortcutSet(new CustomShortcutSet(codeFragment), rootPane);
+ addDisposeRunnable(new Runnable() {
+ public void run() {
+ toStatementAction.unregisterCustomShortcutSet(rootPane);
+ }
+ });
+
+ final AnAction toEvaluationAction = new AnAction() {
+ public void actionPerformed(AnActionEvent e) {
+ getEvaluationPanel().getWatchTree().requestFocus();
+ }
+ };
+ toEvaluationAction.registerCustomShortcutSet(new CustomShortcutSet(resultStroke), rootPane);
+ addDisposeRunnable(new Runnable() {
+ public void run() {
+ toEvaluationAction.unregisterCustomShortcutSet(rootPane);
+ }
+ });
+
+ final AnAction okAction = new AnAction() {
+ public void actionPerformed(AnActionEvent e) {
+ doOKAction();
+ }
+ };
+ okAction.registerCustomShortcutSet(new CustomShortcutSet(altEnter), rootPane);
+ addDisposeRunnable(new Runnable() {
+ public void run() {
+ okAction.unregisterCustomShortcutSet(rootPane);
+ }
+ });
+
+ final DebuggerEditorImpl editor = getEditor();
+ final DocumentAdapter docListener = new DocumentAdapter() {
+ public void documentChanged(final DocumentEvent e) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ updateSwitchButton(e.getDocument());
+ }
+ });
+ }
+ };
+ editor.addDocumentListener(docListener);
+ addDisposeRunnable(new Runnable() {
+ public void run() {
+ editor.removeDocumentListener(docListener);
+ }
+ });
+
+ init();
+ }
+
+ private void updateSwitchButton(Document document) {
+ PsiDocumentManager.getInstance(getProject()).commitDocument(document);
+ PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
+ if (psiFile == null) {
+ return;
+ }
+ PsiElement[] children = psiFile.getChildren();
+ int nonWhite = 0;
+ for (PsiElement child : children) {
+ if (!(child instanceof PsiWhiteSpace)) {
+ nonWhite++;
+ if (nonWhite > 1) {
+ mySwitchAction.setEnabled(false);
+ return;
+ }
+ }
+ }
+
+ mySwitchAction.setEnabled(true);
+ }
+
+ protected Action[] createActions(){
+ return new Action[]{getOKAction(), getCancelAction(), mySwitchAction, getHelpAction() };
+ }
+
+ protected void doHelpAction() {
+ HelpManager.getInstance().invokeHelp(HelpID.EVALUATE);
+ }
+
+ protected DebuggerEditorImpl createEditor(final CodeFragmentFactory factory) {
+ return new DebuggerStatementEditor(getProject(), PositionUtil.getContextElement(getDebuggerContext()), "evaluation", factory);
+ }
+
+ public void dispose() {
+ try {
+ final DebuggerEditorImpl editor = getEditor();
+ final DimensionService dimensionService = DimensionService.getInstance();
+ dimensionService.setSize(STATEMENT_EDITOR_DIMENSION_KEY, editor.getSize(null), getProject());
+ dimensionService.setSize(EVALUATION_PANEL_DIMENSION_KEY, getEvaluationPanel().getSize(), getProject());
+ }
+ finally {
+ super.dispose();
+ }
+ }
+
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+
+ private DebuggerStatementEditor getStatementEditor() {
+ return (DebuggerStatementEditor)getEditor();
+ }
+
+ private class SwitchAction extends AbstractAction {
+ public SwitchAction() {
+ putValue(NAME, DebuggerBundle.message("action.evaluate.statement.dialog.switch.mode.description"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ final TextWithImports text = getEditor().getText();
+ doCancelAction();
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ EvaluateActionHandler.showEvaluationDialog(getProject(), text, DebuggerSettings.EVALUATE_EXPRESSION);
+ }
+ });
+ }
+ }
+
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java b/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java
new file mode 100644
index 0000000..f825e83
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2000-2011 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.codeInsight.hint.HintUtil;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JVMName;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.EditorTextProvider;
+import com.intellij.debugger.ui.impl.DebuggerTreeRenderer;
+import com.intellij.debugger.ui.impl.InspectDebuggerTree;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.SimpleColoredText;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint;
+import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHintTreeComponent;
+import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType;
+import com.sun.jdi.Method;
+import com.sun.jdi.PrimitiveValue;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author lex
+ * @since Nov 24, 2003
+ */
+public class ValueHint extends AbstractValueHint {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.ValueHint");
+ private PsiElement myCurrentExpression = null;
+ private Value myValueToShow = null;
+
+ private ValueHint(Project project, Editor editor, Point point, ValueHintType type, final PsiElement selectedExpression, final TextRange textRange) {
+ super(project, editor, point, type, textRange);
+ myCurrentExpression = selectedExpression;
+ }
+
+ public static ValueHint createValueHint(Project project, Editor editor, Point point, ValueHintType type) {
+ Trinity<PsiElement, TextRange, Value> trinity = getSelectedExpression(project, editor, point, type);
+ final ValueHint hint = new ValueHint(project, editor, point, type, trinity.getFirst(), trinity.getSecond());
+ hint.myValueToShow = trinity.getThird();
+ return hint;
+ }
+
+ protected boolean canShowHint() {
+ return myCurrentExpression != null;
+ }
+
+
+ @Nullable
+ private ExpressionEvaluator getExpressionEvaluator(DebuggerContextImpl debuggerContext) throws EvaluateException {
+ if (myCurrentExpression instanceof PsiExpression) {
+ return EvaluatorBuilderImpl.getInstance().build(myCurrentExpression, debuggerContext.getSourcePosition());
+ }
+
+ CodeFragmentFactory factory = DebuggerUtilsEx.getEffectiveCodeFragmentFactory(myCurrentExpression);
+ TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myCurrentExpression.getText());
+ if (factory == null) return null;
+ JavaCodeFragment codeFragment = factory.createCodeFragment(textWithImports, myCurrentExpression.getContext(), getProject());
+ codeFragment.forceResolveScope(GlobalSearchScope.allScope(getProject()));
+ return factory.getEvaluatorBuilder().build(codeFragment, debuggerContext.getSourcePosition());
+ }
+
+
+ protected void evaluateAndShowHint() {
+ final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(getProject()).getContext();
+
+ final DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
+ if(debuggerSession == null || !debuggerSession.isPaused()) return;
+
+ try {
+
+ final ExpressionEvaluator evaluator = getExpressionEvaluator(debuggerContext);
+ if (evaluator == null) return;
+
+ debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public Priority getPriority() {
+ return Priority.HIGH;
+ }
+
+ public void threadAction() {
+ try {
+ final EvaluationContextImpl evaluationContext = debuggerContext.createEvaluationContext();
+
+ final String expressionText = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ public String compute() {
+ return myCurrentExpression.getText();
+ }
+ });
+ final TextWithImports text = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expressionText);
+ final Value value = myValueToShow != null? myValueToShow : evaluator.evaluate(evaluationContext);
+
+ final WatchItemDescriptor descriptor = new WatchItemDescriptor(getProject(), text, value);
+ if (!isActiveTooltipApplicable(value) || getType() == ValueHintType.MOUSE_OVER_HINT) {
+ if (getType() == ValueHintType.MOUSE_OVER_HINT) {
+ // force using default renderer for mouse over hint in order to not to call accidentally methods while rendering
+ // otherwise, if the hint is invoked explicitly, show it with the right "auto" renderer
+ descriptor.setRenderer(DebugProcessImpl.getDefaultRenderer(value));
+ }
+ descriptor.updateRepresentation(evaluationContext, new DescriptorLabelListener() {
+ public void labelChanged() {
+ if(getCurrentRange() != null) {
+ if(getType() != ValueHintType.MOUSE_OVER_HINT || descriptor.isValueValid()) {
+ final SimpleColoredText simpleColoredText = DebuggerTreeRenderer.getDescriptorText(debuggerContext, descriptor, true);
+ if (isActiveTooltipApplicable(value)){
+ simpleColoredText.append(" (" + DebuggerBundle.message("active.tooltip.suggestion") + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+ showHint(simpleColoredText, descriptor);
+ }
+ }
+ }
+ });
+ } else {
+ final InspectDebuggerTree tree = getInspectTree(descriptor);
+ showTreePopup(tree, debuggerContext, expressionText, new ValueHintTreeComponent(ValueHint.this, tree, expressionText));
+ }
+ }
+ catch (EvaluateException e) {
+ LOG.debug(e);
+ }
+ }
+
+ });
+ }
+ catch (EvaluateException e) {
+ LOG.debug(e);
+ }
+ }
+
+ private static boolean isActiveTooltipApplicable(final Value value) {
+ return value != null && !(value instanceof PrimitiveValue);
+ }
+
+ public void showTreePopup(final InspectDebuggerTree tree,
+ final DebuggerContextImpl debuggerContext,
+ final String title,
+ final AbstractValueHintTreeComponent<?> component) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ tree.rebuild(debuggerContext);
+ showTreePopup(component, tree, title);
+ }
+ });
+ }
+
+ private void showHint(final SimpleColoredText text, final WatchItemDescriptor descriptor) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ public void run() {
+ if(!isHintHidden()) {
+ JComponent component;
+ if (!isActiveTooltipApplicable(descriptor.getValue())) {
+ component = HintUtil.createInformationLabel(text);
+ }
+ else {
+ component = createExpandableHintComponent(text, new Runnable() {
+ public void run() {
+ final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(getProject()).getContext();
+ final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
+ debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext) {
+ public void threadAction() {
+ descriptor.setRenderer(debugProcess.getAutoRenderer(descriptor));
+ final InspectDebuggerTree tree = getInspectTree(descriptor);
+ final String expressionText = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ @Override
+ public String compute() {
+ return myCurrentExpression.getText();
+ }
+ });
+ showTreePopup(tree, debuggerContext, expressionText,
+ new ValueHintTreeComponent(ValueHint.this, tree, expressionText));
+ }
+ });
+ }
+ });
+ }
+ if (!showHint(component)) return;
+ if(getType() == ValueHintType.MOUSE_CLICK_HINT) {
+ HintUtil.createInformationLabel(text).requestFocusInWindow();
+ }
+ }
+ }
+ });
+ }
+
+ private InspectDebuggerTree getInspectTree(final WatchItemDescriptor descriptor) {
+ final InspectDebuggerTree tree = new InspectDebuggerTree(getProject());
+ tree.getModel().addTreeModelListener(createTreeListener(tree));
+ tree.setInspectDescriptor(descriptor);
+ return tree;
+ }
+
+ @Nullable
+ private static Pair<PsiElement, TextRange> findExpression(PsiElement element, boolean allowMethodCalls) {
+ final EditorTextProvider textProvider = EditorTextProvider.EP.forLanguage(element.getLanguage());
+ if (textProvider != null) {
+ return textProvider.findExpression(element, allowMethodCalls);
+ }
+ return null;
+ }
+
+ private static Trinity<PsiElement, TextRange, Value> getSelectedExpression(final Project project, final Editor editor, final Point point, final ValueHintType type) {
+ final Ref<PsiElement> selectedExpression = Ref.create(null);
+ final Ref<TextRange> currentRange = Ref.create(null);
+ final Ref<Value> preCalculatedValue = Ref.create(null);
+
+ PsiDocumentManager.getInstance(project).commitAndRunReadAction(new Runnable() {
+ public void run() {
+ // Point -> offset
+ final int offset = calculateOffset(editor, point);
+
+
+ PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+
+ if(psiFile == null || !psiFile.isValid()) {
+ return;
+ }
+
+ int selectionStart = editor.getSelectionModel().getSelectionStart();
+ int selectionEnd = editor.getSelectionModel().getSelectionEnd();
+
+ if((type == ValueHintType.MOUSE_CLICK_HINT || type == ValueHintType.MOUSE_ALT_OVER_HINT) && (selectionStart <= offset && offset <= selectionEnd)) {
+ PsiElement ctx = (selectionStart > 0) ? psiFile.findElementAt(selectionStart - 1) : psiFile.findElementAt(selectionStart);
+ try {
+ String text = editor.getSelectionModel().getSelectedText();
+ if(text != null && ctx != null) {
+ final JVMElementFactory factory = JVMElementFactories.getFactory(ctx.getLanguage(), project);
+ if (factory == null) {
+ return;
+ }
+ selectedExpression.set(factory.createExpressionFromText(text, ctx));
+ currentRange.set(new TextRange(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd()));
+ }
+ }
+ catch (IncorrectOperationException ignored) {
+ }
+ }
+
+ if(currentRange.get() == null) {
+ PsiElement elementAtCursor = psiFile.findElementAt(offset);
+ if (elementAtCursor == null) {
+ return;
+ }
+ Pair<PsiElement, TextRange> pair = findExpression(elementAtCursor, type == ValueHintType.MOUSE_CLICK_HINT || type == ValueHintType.MOUSE_ALT_OVER_HINT);
+ if (pair == null) {
+ if (type == ValueHintType.MOUSE_OVER_HINT) {
+ final DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession();
+ if(debuggerSession != null && debuggerSession.isPaused()) {
+ final Pair<Method, Value> lastExecuted = debuggerSession.getProcess().getLastExecutedMethod();
+ if (lastExecuted != null) {
+ final Method method = lastExecuted.getFirst();
+ if (method != null) {
+ final Pair<PsiElement, TextRange> expressionPair = findExpression(elementAtCursor, true);
+ if (expressionPair != null && expressionPair.getFirst() instanceof PsiMethodCallExpression) {
+ final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expressionPair.getFirst();
+ final PsiMethod psiMethod = methodCallExpression.resolveMethod();
+ if (psiMethod != null) {
+ final JVMName jvmSignature = JVMNameUtil.getJVMSignature(psiMethod);
+ try {
+ if (method.name().equals(psiMethod.getName()) && method.signature().equals(jvmSignature.getName(debuggerSession.getProcess()))) {
+ pair = expressionPair;
+ preCalculatedValue.set(lastExecuted.getSecond());
+ }
+ }
+ catch (EvaluateException ignored) {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (pair == null) {
+ return;
+ }
+ selectedExpression.set(pair.getFirst());
+ currentRange.set(pair.getSecond());
+ }
+ }
+ });
+ return Trinity.create(selectedExpression.get(), currentRange.get(), preCalculatedValue.get());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/ValueHintTreeComponent.java b/java/debugger/impl/src/com/intellij/debugger/ui/ValueHintTreeComponent.java
new file mode 100644
index 0000000..9902dfc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/ValueHintTreeComponent.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.InspectDebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeExpression;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
+import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHintTreeComponent;
+
+/**
+ * @author nik
+*/
+class ValueHintTreeComponent extends AbstractValueHintTreeComponent<Pair<NodeDescriptorImpl, String>> {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.ValueHintTreeComponent");
+ private final ValueHint myValueHint;
+ private final InspectDebuggerTree myTree;
+
+ public ValueHintTreeComponent(final ValueHint valueHint, InspectDebuggerTree tree, final String title) {
+ super(valueHint, tree, Pair.create(tree.getInspectDescriptor(), title));
+ myValueHint = valueHint;
+ myTree = tree;
+ }
+
+
+ protected void updateTree(Pair<NodeDescriptorImpl, String> descriptorWithTitle){
+ final NodeDescriptorImpl descriptor = descriptorWithTitle.first;
+ final String title = descriptorWithTitle.second;
+ final DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(myValueHint.getProject())).getContext();
+ context.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(context) {
+ public void threadAction() {
+ myTree.setInspectDescriptor(descriptor);
+ myValueHint.showTreePopup(myTree, context, title, ValueHintTreeComponent.this);
+ }
+ });
+ }
+
+
+ protected void setNodeAsRoot(final Object node) {
+ if (node instanceof DebuggerTreeNodeImpl) {
+ myValueHint.shiftLocation();
+ final DebuggerTreeNodeImpl debuggerTreeNode = (DebuggerTreeNodeImpl)node;
+ final DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(myValueHint.getProject())).getContext();
+ context.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(context) {
+ public void threadAction() {
+ try {
+ final NodeDescriptorImpl descriptor = debuggerTreeNode.getDescriptor();
+ final TextWithImports evaluationText = DebuggerTreeNodeExpression.createEvaluationText(debuggerTreeNode, context);
+ final String title = evaluationText.getText();
+ addToHistory(Pair.create(descriptor, title));
+ myTree.setInspectDescriptor(descriptor);
+ myValueHint.showTreePopup(myTree, context, title, ValueHintTreeComponent.this);
+ }
+ catch (final EvaluateException e1) {
+ LOG.debug(e1);
+ }
+ }
+ });
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/WeakMouseListener.java b/java/debugger/impl/src/com/intellij/debugger/ui/WeakMouseListener.java
new file mode 100644
index 0000000..c4b5ed0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/WeakMouseListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.util.WeakListener;
+
+import javax.swing.*;
+import java.awt.event.MouseListener;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 25, 2004
+ */
+public class WeakMouseListener extends WeakListener<JComponent, MouseListener> {
+ public WeakMouseListener(JComponent source, MouseListener listenerImpl) {
+ super(source, MouseListener.class, listenerImpl);
+ }
+ public void addListener(JComponent source, MouseListener listener) {
+ source.addMouseListener(listener);
+ }
+ public void removeListener(JComponent source, MouseListener listener) {
+ source.removeMouseListener(listener);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/WeakMouseMotionListener.java b/java/debugger/impl/src/com/intellij/debugger/ui/WeakMouseMotionListener.java
new file mode 100644
index 0000000..94eb402
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/WeakMouseMotionListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.util.WeakListener;
+
+import javax.swing.*;
+import java.awt.event.MouseMotionListener;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 25, 2004
+ */
+public class WeakMouseMotionListener extends WeakListener<JComponent, MouseMotionListener> {
+ public WeakMouseMotionListener(JComponent source, MouseMotionListener listenerImpl) {
+ super(source, MouseMotionListener.class, listenerImpl);
+ }
+ public void addListener(JComponent source, MouseMotionListener listener) {
+ source.addMouseMotionListener(listener);
+ }
+ public void removeListener(JComponent source, MouseMotionListener listener) {
+ source.removeMouseMotionListener(listener);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java
new file mode 100644
index 0000000..032b480
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointFactory;
+import com.intellij.openapi.util.Key;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup;
+import com.intellij.xdebugger.impl.breakpoints.ui.grouping.XBreakpointTypeGroup;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: zajac
+ * Date: 23.05.12
+ * Time: 16:22
+ * To change this template use File | Settings | File Templates.
+ */
+public class XBreakpointCategoryGroup extends XBreakpointGroup {
+ private Key<? extends Breakpoint> myCategory;
+ private Icon myIcon;
+ private final String myName;
+
+ public XBreakpointCategoryGroup(BreakpointFactory factory) {
+ myCategory = factory.getBreakpointCategory();
+ myIcon = factory.getIcon();
+ final String name = factory.getDisplayName();
+ myName = name != null ? name : "UNKNOWN";
+ }
+
+ public Key<? extends Breakpoint> getCategory() {
+ return myCategory;
+ }
+
+ @Override
+ public Icon getIcon(boolean isOpen) {
+ return myIcon;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return myName;
+ }
+
+ @Override
+ public int compareTo(XBreakpointGroup o) {
+ if (o instanceof XBreakpointTypeGroup) {
+ return -1;
+ }
+ if (o instanceof XBreakpointCategoryGroup) {
+ return getFactoryIndex() - ((XBreakpointCategoryGroup)o).getFactoryIndex();
+ }
+ return super.compareTo(o);
+ }
+
+ private int getFactoryIndex() {
+ BreakpointFactory[] breakpointFactories = BreakpointFactory.getBreakpointFactories();
+ for (int i = 0; i < breakpointFactories.length; ++i) {
+ if (breakpointFactories[i].getBreakpointCategory().equals(myCategory)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointClassGroup.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointClassGroup.java
new file mode 100644
index 0000000..5a81258
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointClassGroup.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PlatformIcons;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class XBreakpointClassGroup extends XBreakpointGroup {
+ private static final String DEFAULT_PACKAGE_NAME = DebuggerBundle.message("default.package.name");
+
+ private String myPackageName;
+ private String myClassName;
+
+ public XBreakpointClassGroup(@Nullable String packageName, String className) {
+ myPackageName = packageName != null ? packageName : DEFAULT_PACKAGE_NAME;
+ myClassName = className;
+ }
+
+ @Override
+ public Icon getIcon(boolean isOpen) {
+ return PlatformIcons.CLASS_ICON;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return getClassName();
+ }
+
+ @NotNull
+ public String getPackageName() {
+ return myPackageName;
+ }
+
+ @NotNull
+ public String getClassName() {
+ return myClassName;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java
new file mode 100644
index 0000000..f3cf9b9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.ui.breakpoints.AnyExceptionBreakpoint;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointFactory;
+import com.intellij.debugger.ui.breakpoints.ExceptionBreakpoint;
+import com.intellij.openapi.util.Key;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+class XBreakpointGroupingByCategoryRule<B> extends XBreakpointGroupingRule<B, XBreakpointCategoryGroup> {
+ XBreakpointGroupingByCategoryRule() {
+ super("XBreakpointGroupingByCategoryRule", "Type");
+ }
+
+ @Override
+ public boolean isAlwaysEnabled() {
+ return true;
+ }
+
+ @Override
+ public XBreakpointCategoryGroup getGroup(@NotNull B b, @NotNull Collection<XBreakpointCategoryGroup> groups) {
+ if (b instanceof Breakpoint) {
+ final Breakpoint breakpoint = (Breakpoint)b;
+ Key<? extends Breakpoint> category = breakpoint.getCategory();
+ if (category.equals(AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT)) {
+ category = ExceptionBreakpoint.CATEGORY;
+ }
+ for (XBreakpointCategoryGroup group : groups) {
+ if (group.getCategory().equals(category)) {
+ return group;
+ }
+ }
+ final BreakpointFactory factory = BreakpointFactory.getInstance(category);
+ if (factory != null) {
+ return new XBreakpointCategoryGroup(factory);
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByClassRule.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByClassRule.java
new file mode 100644
index 0000000..aa28158
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByClassRule.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointFactory;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+class XBreakpointGroupingByClassRule<B> extends XBreakpointGroupingRule<B, XBreakpointClassGroup> {
+ XBreakpointGroupingByClassRule() {
+ super("XBreakpointGroupingByClassRule", "Group by Class");
+ }
+
+ @Override
+ public boolean isAlwaysEnabled() {
+ return false;
+ }
+
+ @Override
+ public XBreakpointClassGroup getGroup(@NotNull B b, @NotNull Collection<XBreakpointClassGroup> groups) {
+ if (b instanceof Breakpoint) {
+ final Breakpoint breakpoint = (Breakpoint)b;
+ String className = breakpoint.getShortClassName();
+ String packageName = breakpoint.getPackageName();
+ if (className == null) {
+ return null;
+ }
+ for (XBreakpointClassGroup group : groups) {
+ if (group.getClassName().equals(className) && group.getPackageName().equals(packageName)) {
+ return group;
+ }
+ }
+ return new XBreakpointClassGroup(packageName, className);
+ }
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByPackageRule.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByPackageRule.java
new file mode 100644
index 0000000..a9f43cc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByPackageRule.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter;
+import com.intellij.debugger.ui.breakpoints.ExceptionBreakpoint;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+public class XBreakpointGroupingByPackageRule<B> extends XBreakpointGroupingRule<B, XBreakpointPackageGroup> {
+
+ protected XBreakpointGroupingByPackageRule() {
+ super("XBreakpointGroupingByPackageRule", "Group by package");
+ }
+
+ @Override
+ public XBreakpointPackageGroup getGroup(@NotNull B breakpoint, @NotNull Collection<XBreakpointPackageGroup> groups) {
+ String packageName = null;
+ if (breakpoint instanceof BreakpointWithHighlighter) {
+ packageName = ((BreakpointWithHighlighter)breakpoint).getPackageName();
+ }
+ else if (breakpoint instanceof ExceptionBreakpoint) {
+ packageName = ((ExceptionBreakpoint)breakpoint).getPackageName();
+ }
+ if (packageName == null) {
+ return null;
+ }
+ for (XBreakpointPackageGroup group : groups) {
+ if (StringUtil.equals(group.getPackageName(), packageName)) {
+ return group;
+ }
+ }
+ return new XBreakpointPackageGroup(packageName);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointPackageGroup.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointPackageGroup.java
new file mode 100644
index 0000000..0d193a2
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointPackageGroup.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PlatformIcons;
+import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+public class XBreakpointPackageGroup extends XBreakpointGroup {
+ private static final String DEFAULT_PACKAGE_NAME = DebuggerBundle.message("default.package.name");
+
+ private String myPackageName;
+
+ public XBreakpointPackageGroup(String packageName) {
+ myPackageName = packageName;
+ }
+
+ @Override
+ public Icon getIcon(boolean isOpen) {
+ return PlatformIcons.PACKAGE_ICON;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ String packageName = getPackageName();
+ return StringUtil.isEmpty(packageName) ? DEFAULT_PACKAGE_NAME : packageName;
+ }
+
+ @NotNull
+ public String getPackageName() {
+ return myPackageName;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddFieldBreakpointDialog.form b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddFieldBreakpointDialog.form
new file mode 100644
index 0000000..5db94cf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddFieldBreakpointDialog.form
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.ui.breakpoints.AddFieldBreakpointDialog">
+ <grid id="dbe86" binding="myPanel" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="6">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="74" y="134" width="245" height="152"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="9636d" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myClassChooser">
+ <constraints>
+ <xy x="0" y="26" width="245" height="22"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="c2ef3" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myFieldChooser">
+ <constraints>
+ <xy x="0" y="80" width="245" height="22"/>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <xy id="3cfba" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="0" y="151" width="245" height="1"/>
+ <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="2" fill="1">
+ <minimum-size width="-1" height="1"/>
+ <maximum-size width="-1" height="1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="bevel-raised"/>
+ <children/>
+ </xy>
+ <vspacer id="339be">
+ <constraints>
+ <xy x="117" y="108" width="11" height="24"/>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2"/>
+ </constraints>
+ </vspacer>
+ <component id="e8c64" class="javax.swing.JLabel">
+ <constraints>
+ <xy x="0" y="56" width="69" height="16"/>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.add.field.breakpoint.dialog.field.name"/>
+ </properties>
+ </component>
+ <component id="c159b" class="javax.swing.JLabel">
+ <constraints>
+ <xy x="0" y="2" width="181" height="16"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.add.field.breakpoint.dialog.fq.name"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddFieldBreakpointDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddFieldBreakpointDialog.java
new file mode 100644
index 0000000..f325da1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddFieldBreakpointDialog.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Sep 11, 2002
+ * Time: 5:23:47 PM
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.codeInsight.generation.PsiFieldMember;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.ide.util.MemberChooser;
+import com.intellij.ide.util.TreeClassChooser;
+import com.intellij.ide.util.TreeClassChooserFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+abstract class AddFieldBreakpointDialog extends DialogWrapper {
+ private final Project myProject;
+ private JPanel myPanel;
+ private TextFieldWithBrowseButton myFieldChooser;
+ private TextFieldWithBrowseButton myClassChooser;
+
+ public AddFieldBreakpointDialog(Project project) {
+ super(project, true);
+ myProject = project;
+ setTitle(DebuggerBundle.message("add.field.breakpoint.dialog.title"));
+ init();
+ }
+
+ protected JComponent createCenterPanel() {
+ myClassChooser.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ public void textChanged(DocumentEvent event) {
+ updateUI();
+ }
+ });
+
+ myClassChooser.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ PsiClass currentClass = getSelectedClass();
+ TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject).createAllProjectScopeChooser(DebuggerBundle.message("add.field.breakpoint.dialog.classchooser.title"));
+ if (currentClass != null) {
+ PsiFile containingFile = currentClass.getContainingFile();
+ if (containingFile != null) {
+ PsiDirectory containingDirectory = containingFile.getContainingDirectory();
+ if (containingDirectory != null) {
+ chooser.selectDirectory(containingDirectory);
+ }
+ }
+ }
+ chooser.showDialog();
+ PsiClass selectedClass = chooser.getSelected();
+ if (selectedClass != null) {
+ myClassChooser.setText(selectedClass.getQualifiedName());
+ }
+ }
+ });
+
+ myFieldChooser.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ PsiClass selectedClass = getSelectedClass();
+ if (selectedClass != null) {
+ PsiField[] fields = selectedClass.getFields();
+ MemberChooser<PsiFieldMember> chooser = new MemberChooser<PsiFieldMember>(ContainerUtil.map2Array(fields, PsiFieldMember.class, new Function<PsiField, PsiFieldMember>() {
+ public PsiFieldMember fun(final PsiField s) {
+ return new PsiFieldMember(s);
+ }
+ }), false, false, myProject);
+ chooser.setTitle(DebuggerBundle.message("add.field.breakpoint.dialog.field.chooser.title", fields.length));
+ chooser.setCopyJavadocVisible(false);
+ chooser.show();
+ List<PsiFieldMember> selectedElements = chooser.getSelectedElements();
+ if (selectedElements != null && selectedElements.size() == 1) {
+ PsiField field = selectedElements.get(0).getElement();
+ myFieldChooser.setText(field.getName());
+ }
+ }
+ }
+ });
+ myFieldChooser.setEnabled(false);
+ return myPanel;
+ }
+
+ private void updateUI() {
+ PsiClass selectedClass = getSelectedClass();
+ myFieldChooser.setEnabled(selectedClass != null);
+ }
+
+ private PsiClass getSelectedClass() {
+ final PsiManager psiManager = PsiManager.getInstance(myProject);
+ String classQName = myClassChooser.getText();
+ if ("".equals(classQName)) {
+ return null;
+ }
+ return JavaPsiFacade.getInstance(psiManager.getProject()).findClass(classQName, GlobalSearchScope.allScope(myProject));
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myClassChooser.getTextField();
+ }
+
+ public String getClassName() {
+ return myClassChooser.getText();
+ }
+
+ protected String getDimensionServiceKey(){
+ return "#com.intellij.debugger.ui.breakpoints.BreakpointsConfigurationDialogFactory.BreakpointsConfigurationDialog.AddFieldBreakpointDialog";
+ }
+
+ public String getFieldName() {
+ return myFieldChooser.getText();
+ }
+
+ protected abstract boolean validateData();
+
+ protected void doOKAction() {
+ if(validateData()) {
+ super.doOKAction();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddWildcardBreakpointDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddWildcardBreakpointDialog.java
new file mode 100644
index 0000000..c18acb7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddWildcardBreakpointDialog.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2004-2006 Alexey Efimov
+ *
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 10, 2006
+ */
+public class AddWildcardBreakpointDialog extends DialogWrapper {
+ private JPanel myPanel;
+ private JTextField myClassPatternField;
+ private JTextField myMethodNameField;
+
+ protected AddWildcardBreakpointDialog(Project project) {
+ super(project, true);
+ setTitle("Add Method Breakpoint");
+ init();
+ }
+
+ protected void doOKAction() {
+ if (getClassPattern().length() == 0) {
+ Messages.showErrorDialog(myPanel, "Class pattern not specified");
+ return;
+ }
+ if (getMethodName().length() == 0) {
+ Messages.showErrorDialog(myPanel, "Method name not specified");
+ return;
+ }
+ super.doOKAction();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myClassPatternField;
+ }
+
+ public String getClassPattern() {
+ return myClassPatternField.getText().trim();
+ }
+
+ public String getMethodName() {
+ return myMethodNameField.getText().trim();
+ }
+
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddWildcardBreakpointForm.form b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddWildcardBreakpointForm.form
new file mode 100644
index 0000000..4866c1e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AddWildcardBreakpointForm.form
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.ui.breakpoints.AddWildcardBreakpointDialog">
+ <grid id="9b829" binding="myPanel" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="120" y="73" width="414" height="149"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="ee438" class="javax.swing.JTextField" binding="myClassPatternField">
+ <constraints>
+ <xy x="2" y="27" width="410" height="22"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="4c818" class="javax.swing.JLabel">
+ <constraints>
+ <xy x="2" y="56" width="84" height="16"/>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0"/>
+ </constraints>
+ <properties>
+ <labelFor value="2d944"/>
+ <nextFocusableComponent value=""/>
+ <text value="&Method name:"/>
+ </properties>
+ </component>
+ <component id="2d944" class="javax.swing.JTextField" binding="myMethodNameField">
+ <constraints>
+ <xy x="2" y="79" width="410" height="22"/>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <vspacer id="2b528">
+ <constraints>
+ <xy x="201" y="101" width="11" height="46"/>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0"/>
+ </constraints>
+ </vspacer>
+ <component id="7fca9" class="javax.swing.JLabel">
+ <constraints>
+ <xy x="2" y="4" width="82" height="16"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0"/>
+ </constraints>
+ <properties>
+ <labelFor value="ee438"/>
+ <text value="&Class pattern:"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AnyExceptionBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AnyExceptionBreakpoint.java
new file mode 100644
index 0000000..3d1ff85
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AnyExceptionBreakpoint.java
@@ -0,0 +1,71 @@
+/*
+ * 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 AnyExceptionBreakpoint
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.Key;
+import com.sun.jdi.ReferenceType;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+public class AnyExceptionBreakpoint extends ExceptionBreakpoint {
+ public static final @NonNls Key<AnyExceptionBreakpoint> ANY_EXCEPTION_BREAKPOINT = BreakpointCategory.lookup("breakpoint_any");
+
+ protected AnyExceptionBreakpoint(Project project) {
+ super(project, null, null);
+ ENABLED = false;
+ }
+
+ public Key<AnyExceptionBreakpoint> getCategory() {
+ return ANY_EXCEPTION_BREAKPOINT;
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("breakpoint.any.exception.display.name");
+ }
+
+ public void createRequest(DebugProcessImpl debugProcess) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (!ENABLED || !debugProcess.isAttached() || debugProcess.areBreakpointsMuted() || !debugProcess.getRequestsManager().findRequests(this).isEmpty()) {
+ return;
+ }
+ super.processClassPrepare(debugProcess, null);
+ }
+
+ public void processClassPrepare(DebugProcess debugProcess, ReferenceType refType) {
+ // should be emty - does not make sense for this breakpoint
+ }
+
+ public void readExternal(Element parentNode) throws InvalidDataException {
+ try {
+ super.readExternal(parentNode);
+ }
+ catch (InvalidDataException e) {
+ if(!READ_NO_CLASS_NAME.equals(e.getMessage())) throw e;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AnyExceptionBreakpointFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AnyExceptionBreakpointFactory.java
new file mode 100644
index 0000000..79398de
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/AnyExceptionBreakpointFactory.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.ui.breakpoints.actions.BreakpointPanelAction;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Key;
+import org.jdom.Element;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 26, 2005
+ */
+public class AnyExceptionBreakpointFactory extends BreakpointFactory{
+ public Breakpoint createBreakpoint(Project project, final Element element) {
+ return new AnyExceptionBreakpoint(project);
+ }
+
+ public Icon getIcon() {
+ return AllIcons.Debugger.Db_exception_breakpoint;
+ }
+
+ public Icon getDisabledIcon() {
+ return AllIcons.Debugger.Db_disabled_exception_breakpoint;
+ }
+
+ @Override
+ protected String getHelpID() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String getDisplayName() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public BreakpointPropertiesPanel createBreakpointPropertiesPanel(Project project, boolean compact) {
+ return new ExceptionBreakpointPropertiesPanel(project, compact);
+ }
+
+ @Override
+ protected BreakpointPanelAction[] createBreakpointPanelActions(Project project, DialogWrapper parentDialog) {
+ return new BreakpointPanelAction[0]; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean breakpointCanBeRemoved(Breakpoint breakpoint) {
+ return false;
+ }
+
+ public @Nullable BreakpointPanel createBreakpointPanel(Project project, DialogWrapper parentDialog) {
+ return null;
+ }
+
+ public Key<AnyExceptionBreakpoint> getBreakpointCategory() {
+ return AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java
new file mode 100644
index 0000000..b738089
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java
@@ -0,0 +1,282 @@
+/*
+ * 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 Breakpoint
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.*;
+import com.intellij.debugger.engine.*;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.psi.PsiClass;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.Value;
+import com.sun.jdi.VoidValue;
+import com.sun.jdi.event.LocatableEvent;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public abstract class Breakpoint extends FilteredRequestor implements ClassPrepareRequestor {
+ public boolean ENABLED = true;
+ public boolean LOG_ENABLED = false;
+ public boolean LOG_EXPRESSION_ENABLED = false;
+ private TextWithImports myLogMessage; // an expression to be evaluated and printed
+ @NonNls private static final String LOG_MESSAGE_OPTION_NAME = "LOG_MESSAGE";
+ public static final Breakpoint[] EMPTY_ARRAY = new Breakpoint[0];
+ protected boolean myCachedVerifiedState = false;
+
+ protected Breakpoint(@NotNull Project project) {
+ super(project);
+ myLogMessage = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "");
+ //noinspection AbstractMethodCallInConstructor
+ final BreakpointDefaults defaults = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().getBreakpointDefaults(getCategory());
+ SUSPEND_POLICY = defaults.getSuspendPolicy();
+ CONDITION_ENABLED = defaults.isConditionEnabled();
+ }
+
+ public abstract PsiClass getPsiClass();
+ /**
+ * Request for creating all needed JPDA requests in the specified VM
+ * @param debuggerProcess the requesting process
+ */
+ public abstract void createRequest(DebugProcessImpl debuggerProcess);
+
+ /**
+ * Request for creating all needed JPDA requests in the specified VM
+ * @param debuggerProcess the requesting process
+ */
+ public abstract void processClassPrepare(DebugProcess debuggerProcess, final ReferenceType referenceType);
+
+ public abstract String getDisplayName ();
+
+ public String getShortName() {
+ return getDisplayName();
+ }
+
+ @Nullable
+ public String getClassName() {
+ return null;
+ }
+
+ public void markVerified(boolean isVerified) {
+ myCachedVerifiedState = isVerified;
+ }
+
+ @Nullable
+ public String getShortClassName() {
+ final String className = getClassName();
+ if (className != null) {
+ final int dotIndex = className.lastIndexOf('.');
+ return dotIndex >= 0 && dotIndex + 1 < className.length()? className.substring(dotIndex + 1) : className;
+ }
+ return className;
+ }
+
+ @Nullable
+ public String getPackageName() {
+ return null;
+ }
+
+ public abstract Icon getIcon();
+
+ public abstract void reload();
+
+ /**
+ * returns UI representation
+ */
+ public abstract String getEventMessage(LocatableEvent event);
+
+ public abstract boolean isValid();
+
+ public abstract Key<? extends Breakpoint> getCategory();
+
+ /**
+ * Associates breakpoint with class.
+ * Create requests for loaded class and registers callback for loading classes
+ * @param debugProcess the requesting process
+ */
+ protected void createOrWaitPrepare(DebugProcessImpl debugProcess, String classToBeLoaded) {
+ debugProcess.getRequestsManager().callbackOnPrepareClasses(this, classToBeLoaded);
+
+ List list = debugProcess.getVirtualMachineProxy().classesByName(classToBeLoaded);
+ for (final Object aList : list) {
+ ReferenceType refType = (ReferenceType)aList;
+ if (refType.isPrepared()) {
+ processClassPrepare(debugProcess, refType);
+ }
+ }
+ }
+
+ protected void createOrWaitPrepare(final DebugProcessImpl debugProcess, final SourcePosition classPosition) {
+ debugProcess.getRequestsManager().callbackOnPrepareClasses(this, classPosition);
+
+ List list = debugProcess.getPositionManager().getAllClasses(classPosition);
+ for (final Object aList : list) {
+ ReferenceType refType = (ReferenceType)aList;
+ if (refType.isPrepared()) {
+ processClassPrepare(debugProcess, refType);
+ }
+ }
+ }
+
+ protected ObjectReference getThisObject(SuspendContextImpl context, LocatableEvent event) throws EvaluateException {
+ ThreadReferenceProxyImpl thread = context.getThread();
+ if(thread != null) {
+ StackFrameProxyImpl stackFrameProxy = thread.frame(0);
+ if(stackFrameProxy != null) {
+ return stackFrameProxy.thisObject();
+ }
+ }
+ return null;
+ }
+
+ public boolean processLocatableEvent(final SuspendContextCommandImpl action, final LocatableEvent event) throws EventProcessingException {
+ final SuspendContextImpl context = action.getSuspendContext();
+ if(!isValid()) {
+ context.getDebugProcess().getRequestsManager().deleteRequest(this);
+ return false;
+ }
+
+ final String[] title = {DebuggerBundle.message("title.error.evaluating.breakpoint.condition") };
+
+ try {
+ final StackFrameProxyImpl frameProxy = context.getThread().frame(0);
+ if (frameProxy == null) {
+ // might be if the thread has been collected
+ return false;
+ }
+
+ final EvaluationContextImpl evaluationContext = new EvaluationContextImpl(
+ action.getSuspendContext(),
+ frameProxy,
+ getThisObject(context, event)
+ );
+
+ if(!evaluateCondition(evaluationContext, event)) {
+ return false;
+ }
+
+ title[0] = DebuggerBundle.message("title.error.evaluating.breakpoint.action");
+ runAction(evaluationContext, event);
+ }
+ catch (final EvaluateException ex) {
+ if(ApplicationManager.getApplication().isUnitTestMode()) {
+ System.out.println(ex.getMessage());
+ return false;
+ }
+
+ throw new EventProcessingException(title[0], ex.getMessage(), ex);
+ }
+
+ return true;
+ }
+
+ private void runAction(final EvaluationContextImpl context, LocatableEvent event) {
+ if (LOG_ENABLED || LOG_EXPRESSION_ENABLED) {
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ if (LOG_ENABLED) {
+ buf.append(getEventMessage(event));
+ buf.append("\n");
+ }
+ final DebugProcessImpl debugProcess = context.getDebugProcess();
+ final TextWithImports expressionToEvaluate = getLogMessage();
+ if (LOG_EXPRESSION_ENABLED && expressionToEvaluate != null && !"".equals(expressionToEvaluate.getText())) {
+ if(!debugProcess.isAttached()) {
+ return;
+ }
+
+ try {
+ ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(getProject(), new EvaluatingComputable<ExpressionEvaluator>() {
+ public ExpressionEvaluator compute() throws EvaluateException {
+ return EvaluatorBuilderImpl.build(expressionToEvaluate, ContextUtil.getContextElement(context), ContextUtil.getSourcePosition(context));
+ }
+ });
+ final Value eval = evaluator.evaluate(context);
+ final String result = eval instanceof VoidValue ? "void" : DebuggerUtils.getValueAsString(context, eval);
+ buf.append(result);
+ }
+ catch (EvaluateException e) {
+ buf.append(DebuggerBundle.message("error.unable.to.evaluate.expression"));
+ buf.append(" \"");
+ buf.append(expressionToEvaluate);
+ buf.append("\"");
+ buf.append(" : ");
+ buf.append(e.getMessage());
+ }
+ buf.append("\n");
+ }
+ if (buf.length() > 0) {
+ debugProcess.printToConsole(buf.toString());
+ }
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+ }
+
+ public final void updateUI() {
+ updateUI(EmptyRunnable.getInstance());
+ }
+
+ public void updateUI(@NotNull Runnable afterUpdate) {
+ }
+
+ public void delete() {
+ RequestManagerImpl.deleteRequests(this);
+ }
+
+ public void readExternal(Element parentNode) throws InvalidDataException {
+ super.readExternal(parentNode);
+ String logMessage = JDOMExternalizerUtil.readField(parentNode, LOG_MESSAGE_OPTION_NAME);
+ if (logMessage != null) {
+ setLogMessage(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, logMessage));
+ }
+ }
+
+ public void writeExternal(Element parentNode) throws WriteExternalException {
+ super.writeExternal(parentNode);
+ JDOMExternalizerUtil.writeField(parentNode, LOG_MESSAGE_OPTION_NAME, getLogMessage().toExternalForm());
+ }
+
+ public TextWithImports getLogMessage() {
+ return myLogMessage;
+ }
+
+ public void setLogMessage(TextWithImports logMessage) {
+ myLogMessage = logMessage;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointCategory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointCategory.java
new file mode 100644
index 0000000..ec7fbe0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointCategory.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.openapi.util.Key;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 18, 2007
+ */
+public final class BreakpointCategory {
+ private static final Map<String, Key> ourMap = new HashMap<String, Key>();
+
+ private BreakpointCategory() {
+ }
+
+ @NotNull
+ public static <T extends Breakpoint> Key<T> lookup(String name) {
+ Key<T> key = ourMap.get(name);
+ if (key == null) {
+ key = Key.create(name);
+ ourMap.put(name, key);
+ }
+ return key;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointDefaults.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointDefaults.java
new file mode 100644
index 0000000..89b5f9c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointDefaults.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.settings.DebuggerSettings;
+import org.jetbrains.annotations.NotNull;
+
+/**
+* @author Eugene Zhuravlev
+* Date: 8/30/12
+*/
+public final class BreakpointDefaults {
+ private String mySuspendPolicy = DebuggerSettings.SUSPEND_ALL;
+ private boolean myIsConditionEnabled = true;
+
+ public BreakpointDefaults() {
+ }
+
+ public BreakpointDefaults(String suspendPolicy, boolean conditionEnabled) {
+ setSuspendPolicy(suspendPolicy);
+ this.myIsConditionEnabled = conditionEnabled;
+ }
+
+ @NotNull
+ public String getSuspendPolicy() {
+ return mySuspendPolicy;
+ }
+
+ public void setSuspendPolicy(String suspendPolicy) {
+ if (DebuggerSettings.SUSPEND_THREAD.equals(suspendPolicy) || DebuggerSettings.SUSPEND_ALL.equals( suspendPolicy)) {
+ mySuspendPolicy = suspendPolicy;
+ }
+ }
+
+ public boolean isConditionEnabled() {
+ return myIsConditionEnabled;
+ }
+
+ public void setConditionEnabled(boolean isConditionEnabled) {
+ myIsConditionEnabled = isConditionEnabled;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointFactory.java
new file mode 100644
index 0000000..6389fdd
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointFactory.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.ui.breakpoints.actions.BreakpointPanelAction;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Key;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem;
+import org.jdom.Element;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * Used to deexternalize breakpoints of certain category while reading saved configuration and for creating configuration UI
+ */
+public abstract class BreakpointFactory {
+ public static final ExtensionPointName<BreakpointFactory> EXTENSION_POINT_NAME =
+ ExtensionPointName.create("com.intellij.debugger.breakpointFactory");
+
+ public static BreakpointFactory[] getBreakpointFactories() {
+ return ApplicationManager.getApplication().getExtensions(EXTENSION_POINT_NAME);
+ }
+
+ public abstract Breakpoint createBreakpoint(Project project, final Element element);
+
+ public abstract Key<? extends Breakpoint> getBreakpointCategory();
+
+ public BreakpointPanel createBreakpointPanel(final Project project, final DialogWrapper parentDialog) {
+ BreakpointPanel panel =
+ new BreakpointPanel(project, createBreakpointPropertiesPanel(project, false), createBreakpointPanelActions(project, parentDialog),
+ getBreakpointCategory(), getDisplayName(), getHelpID());
+ configureBreakpointPanel(panel);
+ return panel;
+ }
+
+ public abstract Icon getIcon();
+
+ public abstract Icon getDisabledIcon();
+
+ @Nullable
+ public static BreakpointFactory getInstance(Key<? extends Breakpoint> category) {
+ final BreakpointFactory[] allFactories = getBreakpointFactories();
+ for (final BreakpointFactory factory : allFactories) {
+ if (category.equals(factory.getBreakpointCategory())) {
+ return factory;
+ }
+ }
+ return null;
+ }
+
+ protected void configureBreakpointPanel(BreakpointPanel panel) {
+ }
+
+ protected abstract String getHelpID();
+
+ public abstract String getDisplayName();
+
+ @Nullable
+ public abstract BreakpointPropertiesPanel createBreakpointPropertiesPanel(Project project, boolean compact);
+
+ protected abstract BreakpointPanelAction[] createBreakpointPanelActions(Project project, DialogWrapper parentDialog);
+
+ @Nullable
+ public Breakpoint addBreakpoint(Project project) {
+ return null;
+ }
+
+ public boolean canAddBreakpoints() {
+ return false;
+ }
+
+ public boolean breakpointCanBeRemoved(Breakpoint breakpoint) {
+ return true;
+ }
+
+ public BreakpointItem createBreakpointItem(final Breakpoint breakpoint) {
+ return new JavaBreakpointItem(this, breakpoint);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java
new file mode 100644
index 0000000..8c77bc1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java
@@ -0,0 +1,1100 @@
+/*
+ * 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 BreakpointManager
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.codeInsight.folding.impl.actions.ExpandRegionAction;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextListener;
+import com.intellij.debugger.impl.DebuggerManagerImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.event.*;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.editor.markup.MarkupEditorFilterFactory;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.TextEditor;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.util.*;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.Alarm;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.IJSwingUtilities;
+import com.intellij.util.containers.HashMap;
+import com.intellij.xdebugger.XDebuggerUtil;
+import com.intellij.xdebugger.impl.DebuggerSupport;
+import com.intellij.xdebugger.impl.XDebugSessionImpl;
+import com.sun.jdi.Field;
+import com.sun.jdi.InternalException;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.request.*;
+import gnu.trove.TIntHashSet;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.MouseEvent;
+import java.util.*;
+
+public class BreakpointManager implements JDOMExternalizable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.BreakpointManager");
+
+ @NonNls private static final String RULES_GROUP_NAME = "breakpoint_rules";
+ private final Project myProject;
+ private AnyExceptionBreakpoint myAnyExceptionBreakpoint;
+ private final List<Breakpoint> myBreakpoints = new ArrayList<Breakpoint>(); // breakpoints storage, access should be synchronized
+ private final List<EnableBreakpointRule> myBreakpointRules = new ArrayList<EnableBreakpointRule>(); // breakpoint rules
+ @Nullable private List<Breakpoint> myBreakpointsListForIteration = null; // another list for breakpoints iteration, unsynchronized access ok
+ private final Map<Document, List<BreakpointWithHighlighter>> myDocumentBreakpoints = new HashMap<Document, List<BreakpointWithHighlighter>>();
+ private final Map<String, String> myUIProperties = new java.util.HashMap<String, String>();
+ private final Map<Key<? extends Breakpoint>, BreakpointDefaults> myBreakpointDefaults = new HashMap<Key<? extends Breakpoint>, BreakpointDefaults>();
+
+ private final EventDispatcher<BreakpointManagerListener> myDispatcher = EventDispatcher.create(BreakpointManagerListener.class);
+
+ private final StartupManager myStartupManager;
+
+ @NonNls private static final String MASTER_BREAKPOINT_TAGNAME = "master_breakpoint";
+ @NonNls private static final String SLAVE_BREAKPOINT_TAGNAME = "slave_breakpoint";
+ @NonNls private static final String DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME = "default_suspend_policy";
+ @NonNls private static final String DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME = "default_condition_enabled";
+
+ private void update(@NotNull List<BreakpointWithHighlighter> breakpoints) {
+ final TIntHashSet intHash = new TIntHashSet();
+
+ for (BreakpointWithHighlighter breakpoint : breakpoints) {
+ SourcePosition sourcePosition = breakpoint.getSourcePosition();
+ breakpoint.reload();
+
+ if (breakpoint.isValid()) {
+ if (breakpoint.getSourcePosition().getLine() != sourcePosition.getLine()) {
+ fireBreakpointChanged(breakpoint);
+ }
+
+ if (intHash.contains(breakpoint.getLineIndex())) {
+ remove(breakpoint);
+ }
+ else {
+ intHash.add(breakpoint.getLineIndex());
+ }
+ }
+ else {
+ remove(breakpoint);
+ }
+ }
+ }
+
+ /*
+ // todo: not needed??
+ private void setInvalid(final BreakpointWithHighlighter breakpoint) {
+ Collection<DebuggerSession> sessions = DebuggerManagerEx.getInstanceEx(myProject).getSessions();
+
+ for (Iterator<DebuggerSession> iterator = sessions.getSectionsIterator(); getSectionsIterator.hasNext();) {
+ DebuggerSession session = iterator.next();
+ final DebugProcessImpl process = session.getProcess();
+ process.getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ process.getRequestsManager().deleteRequest(breakpoint);
+ process.getRequestsManager().setInvalid(breakpoint, "Source code changed");
+ breakpoint.updateUI();
+ }
+ });
+ }
+ }
+ */
+
+ private void remove(final BreakpointWithHighlighter breakpoint) {
+ DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
+ @Override
+ public void run() {
+ removeBreakpoint(breakpoint);
+ }
+ });
+ }
+
+ public BreakpointManager(@NotNull Project project, @NotNull StartupManager startupManager, @NotNull DebuggerManagerImpl debuggerManager) {
+ myProject = project;
+ myStartupManager = startupManager;
+ debuggerManager.getContextManager().addListener(new DebuggerContextListener() {
+ private DebuggerSession myPreviousSession;
+
+ @Override
+ public void changeEvent(@NotNull DebuggerContextImpl newContext, int event) {
+ if (newContext.getDebuggerSession() != myPreviousSession || event == DebuggerSession.EVENT_DETACHED) {
+ updateBreakpointsUI();
+ myPreviousSession = newContext.getDebuggerSession();
+ }
+ }
+ });
+ }
+
+ public void init() {
+ EditorEventMulticaster eventMulticaster = EditorFactory.getInstance().getEventMulticaster();
+ EditorMouseAdapter myEditorMouseListener = new EditorMouseAdapter() {
+ @Nullable private EditorMouseEvent myMousePressedEvent;
+
+ @Nullable
+ private Breakpoint toggleBreakpoint(final boolean mostSuitingBreakpoint, final int line) {
+ final Editor editor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
+ if (editor == null) {
+ return null;
+ }
+ final Document document = editor.getDocument();
+ final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
+ if (psiFile == null) {
+ return null;
+ }
+ final FileType fileType = psiFile.getFileType();
+ boolean isInsideCompiledClass = StdFileTypes.CLASS.equals(fileType);
+ if (!isInsideCompiledClass && !(DebuggerUtils.supportsJVMDebugging(fileType) || DebuggerUtils.supportsJVMDebugging(psiFile))) {
+ return null;
+ }
+ PsiDocumentManager.getInstance(myProject).commitDocument(document);
+
+ int offset = editor.getCaretModel().getOffset();
+ int editorLine = editor.getDocument().getLineNumber(offset);
+ if (editorLine != line) {
+ if (line < 0 || line >= document.getLineCount()) {
+ return null;
+ }
+ offset = editor.getDocument().getLineStartOffset(line);
+ }
+
+ ExpandRegionAction.expandRegionAtCaret(myProject, editor);
+
+ Breakpoint breakpoint = findBreakpoint(document, offset, null);
+ if (breakpoint == null) {
+ if (mostSuitingBreakpoint || isInsideCompiledClass) {
+ breakpoint = addFieldBreakpoint(document, offset);
+ if (breakpoint == null) {
+ breakpoint = addMethodBreakpoint(document, line);
+ }
+ if (breakpoint == null && !isInsideCompiledClass) {
+ breakpoint = addLineBreakpoint(document, line);
+ }
+ }
+ else {
+ breakpoint = addLineBreakpoint(document, line);
+
+ if (breakpoint == null) {
+ breakpoint = addMethodBreakpoint(document, line);
+ }
+ }
+
+ if (breakpoint != null) {
+ RequestManagerImpl.createRequests(breakpoint);
+ }
+ return breakpoint;
+ }
+ else {
+ removeBreakpoint(breakpoint);
+ return null;
+ }
+ }
+
+ private boolean isFromMyProject(Editor editor) {
+ FileEditor[] allEditors = FileEditorManager.getInstance(myProject).getAllEditors();
+ for (FileEditor ed : allEditors) {
+ if (!(ed instanceof TextEditor)) {
+ continue;
+ }
+ if (((TextEditor)ed).getEditor().equals(editor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //mousePressed + mouseReleased is a hack to keep selection in editor when shift is pressed
+ @Override
+ public void mousePressed(@NotNull EditorMouseEvent e) {
+ if (MarkupEditorFilterFactory.createIsDiffFilter().avaliableIn(e.getEditor())) return;
+
+ if (e.isConsumed()) return;
+
+ if (e.getArea() == EditorMouseEventArea.LINE_MARKERS_AREA && e.getMouseEvent().isShiftDown()) {
+ myMousePressedEvent = e;
+ e.consume();
+ }
+ }
+
+ @Override
+ public void mouseReleased(@NotNull EditorMouseEvent e) {
+ if (myMousePressedEvent != null) {
+ mouseClicked(e);
+ }
+ myMousePressedEvent = null;
+ }
+
+ @Override
+ public void mouseClicked(@NotNull final EditorMouseEvent e) {
+ if (MarkupEditorFilterFactory.createIsDiffFilter().avaliableIn(e.getEditor())) return;
+
+ if (e.isConsumed()) return;
+
+ if (e.getArea() == EditorMouseEventArea.LINE_MARKERS_AREA) {
+ PsiDocumentManager.getInstance(myProject).commitAndRunReadAction(new Runnable() {
+ @Override
+ public void run() {
+ final Editor editor = e.getEditor();
+ if (!isFromMyProject(editor)) {
+ return;
+ }
+ final int line = editor.xyToLogicalPosition(e.getMouseEvent().getPoint()).line;
+ final Document document = editor.getDocument();
+ if (line < 0 || line >= document.getLineCount()) {
+ return;
+ }
+ MouseEvent event = e.getMouseEvent();
+ if (event.isPopupTrigger()) {
+ return;
+ }
+ if (event.getButton() != 1) {
+ return;
+ }
+ if (e.getMouseEvent().isControlDown() || e.getMouseEvent().isMetaDown()) {
+ return;
+ }
+
+ if (XDebuggerUtil.getInstance().canPutBreakpointAt(myProject, FileDocumentManager.getInstance().getFile(document), line)) {
+ return;
+ }
+ e.consume();
+
+ DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
+ @Override
+ public void run() {
+ final Breakpoint breakpoint = toggleBreakpoint(e.getMouseEvent().isAltDown(), line);
+
+
+ if (e.getMouseEvent().isShiftDown() && breakpoint != null) {
+ breakpoint.LOG_EXPRESSION_ENABLED = true;
+ String selection = editor.getSelectionModel().getSelectedText();
+ String text = selection != null ? selection : DebuggerBundle.message("breakpoint.log.message",
+ breakpoint.getDisplayName());
+ breakpoint.setLogMessage(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, text));
+ breakpoint.SUSPEND = false;
+ editBreakpoint(breakpoint, editor);
+
+
+ //DialogWrapper dialog = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager()
+ // .createConfigurationDialog(breakpoint, BreakpointPropertiesPanel.CONTROL_LOG_MESSAGE);
+ //dialog.show();
+ //
+ //if (!dialog.isOK()) {
+ // removeBreakpoint(breakpoint);
+ //}
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+ };
+
+ eventMulticaster.addEditorMouseListener(myEditorMouseListener, myProject);
+
+ final DocumentListener myDocumentListener = new DocumentAdapter() {
+ private final Alarm myUpdateAlarm = new Alarm();
+
+ @Override
+ public void documentChanged(@NotNull final DocumentEvent e) {
+ final Document document = e.getDocument();
+ synchronized (BreakpointManager.this) {
+ List<BreakpointWithHighlighter> breakpoints = myDocumentBreakpoints.get(document);
+
+ if(breakpoints != null) {
+ myUpdateAlarm.cancelAllRequests();
+ // must create new array in order to avoid "concurrent modification" errors
+ final List<BreakpointWithHighlighter> breakpointsToUpdate = new ArrayList<BreakpointWithHighlighter>(breakpoints);
+ myUpdateAlarm.addRequest(new Runnable() {
+ @Override
+ public void run() {
+ if (!myProject.isDisposed()) {
+ PsiDocumentManager.getInstance(myProject).commitDocument(document);
+ update(breakpointsToUpdate);
+ }
+ }
+ }, 300, ModalityState.NON_MODAL);
+ }
+ }
+ }
+ };
+
+ eventMulticaster.addDocumentListener(myDocumentListener, myProject);
+ }
+
+ public void editBreakpoint(final Breakpoint breakpoint, final Editor editor) {
+ DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
+ @Override
+ public void run() {
+ final GutterIconRenderer renderer = ((BreakpointWithHighlighter)breakpoint).getHighlighter().getGutterIconRenderer();
+ if (renderer != null) {
+ DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class).getEditBreakpointAction()
+ .editBreakpoint(myProject, editor, breakpoint, renderer);
+ }
+ }
+ });
+ }
+
+ @NotNull
+ public BreakpointDefaults getBreakpointDefaults(Key<? extends Breakpoint> category) {
+ BreakpointDefaults defaults = myBreakpointDefaults.get(category);
+ if (defaults == null) {
+ defaults = new BreakpointDefaults();
+ }
+ return defaults;
+ }
+
+ public void setBreakpointDefaults(Key<? extends Breakpoint> category, BreakpointDefaults defaults) {
+ myBreakpointDefaults.put(category, defaults);
+ }
+
+
+ @Nullable
+ public RunToCursorBreakpoint addRunToCursorBreakpoint(Document document, int lineIndex, final boolean ignoreBreakpoints) {
+ return RunToCursorBreakpoint.create(myProject, document, lineIndex, ignoreBreakpoints);
+ }
+
+ @Nullable
+ public LineBreakpoint addLineBreakpoint(Document document, int lineIndex) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ if (!LineBreakpoint.canAddLineBreakpoint(myProject, document, lineIndex)) {
+ return null;
+ }
+
+ LineBreakpoint breakpoint = LineBreakpoint.create(myProject, document, lineIndex);
+ if (breakpoint == null) {
+ return null;
+ }
+
+ addBreakpoint(breakpoint);
+ return breakpoint;
+ }
+
+ @Nullable
+ public FieldBreakpoint addFieldBreakpoint(Field field, ObjectReference object) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ final FieldBreakpoint fieldBreakpoint = FieldBreakpoint.create(myProject, field, object);
+ if (fieldBreakpoint != null) {
+ addBreakpoint(fieldBreakpoint);
+ }
+ return fieldBreakpoint;
+ }
+
+ @Nullable
+ public FieldBreakpoint addFieldBreakpoint(@NotNull Document document, int offset) {
+ PsiField field = FieldBreakpoint.findField(myProject, document, offset);
+ if (field == null) {
+ return null;
+ }
+
+ int line = document.getLineNumber(offset);
+
+ if (document.getLineNumber(field.getNameIdentifier().getTextOffset()) < line) {
+ return null;
+ }
+
+ return addFieldBreakpoint(document, line, field.getName());
+ }
+
+ @Nullable
+ public FieldBreakpoint addFieldBreakpoint(Document document, int lineIndex, String fieldName) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ FieldBreakpoint fieldBreakpoint = FieldBreakpoint.create(myProject, document, lineIndex, fieldName);
+ if (fieldBreakpoint != null) {
+ addBreakpoint(fieldBreakpoint);
+ }
+ return fieldBreakpoint;
+ }
+
+ @NotNull
+ public ExceptionBreakpoint addExceptionBreakpoint(@NotNull String exceptionClassName, String packageName) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ ExceptionBreakpoint breakpoint = new ExceptionBreakpoint(myProject, exceptionClassName, packageName);
+ addBreakpoint(breakpoint);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ExceptionBreakpoint Added");
+ }
+ return breakpoint;
+ }
+
+ @Nullable
+ public MethodBreakpoint addMethodBreakpoint(Document document, int lineIndex) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+
+ MethodBreakpoint breakpoint = MethodBreakpoint.create(myProject, document, lineIndex);
+ if (breakpoint == null) {
+ return null;
+ }
+
+ XDebugSessionImpl.NOTIFICATION_GROUP.createNotification("Method breakpoints may dramatically slow down debugging", MessageType.WARNING).notify(myProject);
+
+ addBreakpoint(breakpoint);
+ return breakpoint;
+ }
+
+ @Nullable
+ public WildcardMethodBreakpoint addMethodBreakpoint(String classPattern, String methodName) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ WildcardMethodBreakpoint breakpoint = WildcardMethodBreakpoint.create(myProject, classPattern, methodName);
+ if (breakpoint == null) {
+ return null;
+ }
+ addBreakpoint(breakpoint);
+ return breakpoint;
+ }
+
+ /**
+ * @return null if not found or a breakpoint object
+ */
+ @NotNull
+ public List<BreakpointWithHighlighter> findBreakpoints(final Document document, final int offset) {
+ LinkedList<BreakpointWithHighlighter> result = new LinkedList<BreakpointWithHighlighter>();
+
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ for (final Breakpoint breakpoint : getBreakpoints()) {
+ if (breakpoint instanceof BreakpointWithHighlighter && ((BreakpointWithHighlighter)breakpoint).isAt(document, offset)) {
+ result.add((BreakpointWithHighlighter)breakpoint);
+ }
+ }
+
+ return result;
+ }
+
+ @NotNull
+ public List<BreakpointWithHighlighter> findBreakpoints(@NotNull Document document, @NotNull TextRange textRange) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ List<BreakpointWithHighlighter> result = new ArrayList<BreakpointWithHighlighter>();
+ int startLine = document.getLineNumber(textRange.getStartOffset());
+ int endLine = document.getLineNumber(textRange.getEndOffset())+1;
+ TextRange lineRange = new TextRange(startLine, endLine);
+ for (final Breakpoint breakpoint : getBreakpoints()) {
+ if (breakpoint instanceof BreakpointWithHighlighter &&
+ lineRange.contains(((BreakpointWithHighlighter)breakpoint).getLineIndex())) {
+ result.add((BreakpointWithHighlighter)breakpoint);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ *
+ * @param document
+ * @param offset
+ * @param category breakpoint's category, null if the category does not matter
+ * @return
+ */
+ @Nullable
+ public <T extends BreakpointWithHighlighter> T findBreakpoint(final Document document, final int offset, @Nullable final Key<T> category) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ for (final Breakpoint breakpoint : getBreakpoints()) {
+ if (breakpoint instanceof BreakpointWithHighlighter && ((BreakpointWithHighlighter)breakpoint).isAt(document, offset)) {
+ if (category == null || category.equals(breakpoint.getCategory())) {
+ return (T)breakpoint;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void readExternal(@NotNull final Element parentNode) throws InvalidDataException {
+ if (myProject.isOpen()) {
+ doRead(parentNode);
+ } else {
+ myStartupManager.registerPostStartupActivity(new Runnable() {
+ @Override
+ public void run() {
+ doRead(parentNode);
+ }
+ });
+ }
+ }
+
+ private void doRead(@NotNull final Element parentNode) {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ @Override
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void run() {
+ final Map<String, Breakpoint> nameToBreakpointMap = new java.util.HashMap<String, Breakpoint>();
+ try {
+ final List groups = parentNode.getChildren();
+ for (final Object group1 : groups) {
+ final Element group = (Element)group1;
+ if (group.getName().equals(RULES_GROUP_NAME)) {
+ continue;
+ }
+ final String categoryName = group.getName();
+ final Key<Breakpoint> breakpointCategory = BreakpointCategory.lookup(categoryName);
+ final String defaultPolicy = group.getAttributeValue(DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME);
+ final boolean conditionEnabled = Boolean.parseBoolean(group.getAttributeValue(DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME, "true"));
+ setBreakpointDefaults(breakpointCategory, new BreakpointDefaults(defaultPolicy, conditionEnabled));
+ Element anyExceptionBreakpointGroup;
+ if (!AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT.equals(breakpointCategory)) {
+ // for compatibility with previous format
+ anyExceptionBreakpointGroup = group.getChild(AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT.toString());
+ final BreakpointFactory factory = BreakpointFactory.getInstance(breakpointCategory);
+ if (factory != null) {
+ for (final Object o : group.getChildren("breakpoint")) {
+ Element breakpointNode = (Element)o;
+ Breakpoint breakpoint = factory.createBreakpoint(myProject, breakpointNode);
+ breakpoint.readExternal(breakpointNode);
+ addBreakpoint(breakpoint);
+ nameToBreakpointMap.put(breakpoint.getDisplayName(), breakpoint);
+ }
+ }
+ }
+ else {
+ anyExceptionBreakpointGroup = group;
+ }
+
+ if (anyExceptionBreakpointGroup != null) {
+ final Element breakpointElement = group.getChild("breakpoint");
+ if (breakpointElement != null) {
+ getAnyExceptionBreakpoint().readExternal(breakpointElement);
+ }
+ }
+
+ }
+ }
+ catch (InvalidDataException ignored) {
+ }
+
+ final Element rulesGroup = parentNode.getChild(RULES_GROUP_NAME);
+ if (rulesGroup != null) {
+ final List rules = rulesGroup.getChildren("rule");
+ for (final Object rule1 : rules) {
+ final Element rule = (Element)rule1;
+ final Element master = rule.getChild(MASTER_BREAKPOINT_TAGNAME);
+ if (master == null) {
+ continue;
+ }
+ final Element slave = rule.getChild(SLAVE_BREAKPOINT_TAGNAME);
+ if (slave == null) {
+ continue;
+ }
+ final Breakpoint masterBreakpoint = nameToBreakpointMap.get(master.getAttributeValue("name"));
+ if (masterBreakpoint == null) {
+ continue;
+ }
+ final Breakpoint slaveBreakpoint = nameToBreakpointMap.get(slave.getAttributeValue("name"));
+ if (slaveBreakpoint == null) {
+ continue;
+ }
+ addBreakpointRule(new EnableBreakpointRule(BreakpointManager.this, masterBreakpoint, slaveBreakpoint, "true".equalsIgnoreCase(rule.getAttributeValue("leaveEnabled"))));
+ }
+ }
+
+ DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
+ @Override
+ public void run() {
+ updateBreakpointsUI();
+ }
+ });
+ }
+ });
+
+ myUIProperties.clear();
+ final Element props = parentNode.getChild("ui_properties");
+ if (props != null) {
+ final List children = props.getChildren("property");
+ for (Object child : children) {
+ Element property = (Element)child;
+ final String name = property.getAttributeValue("name");
+ final String value = property.getAttributeValue("value");
+ if (name != null && value != null) {
+ myUIProperties.put(name, value);
+ }
+ }
+ }
+ }
+
+ //used in Fabrique
+ public synchronized void addBreakpoint(Breakpoint breakpoint) {
+ myBreakpoints.add(breakpoint);
+ myBreakpointsListForIteration = null;
+ if(breakpoint instanceof BreakpointWithHighlighter) {
+ BreakpointWithHighlighter breakpointWithHighlighter = (BreakpointWithHighlighter)breakpoint;
+ Document document = breakpointWithHighlighter.getDocument();
+ if(document != null) {
+ List<BreakpointWithHighlighter> breakpoints = myDocumentBreakpoints.get(document);
+
+ if(breakpoints == null) {
+ breakpoints = new ArrayList<BreakpointWithHighlighter>();
+ myDocumentBreakpoints.put(document, breakpoints);
+ }
+ breakpoints.add(breakpointWithHighlighter);
+ }
+ }
+ myDispatcher.getMulticaster().breakpointsChanged();
+ }
+
+ public synchronized void removeBreakpoint(@Nullable final Breakpoint breakpoint) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ if (breakpoint == null) {
+ return;
+ }
+
+ if (myBreakpoints.remove(breakpoint)) {
+ updateBreakpointRules(breakpoint);
+ myBreakpointsListForIteration = null;
+ if(breakpoint instanceof BreakpointWithHighlighter) {
+ //breakpoint.saveToString() may be invalid
+
+ for (final Document document : myDocumentBreakpoints.keySet()) {
+ final List<BreakpointWithHighlighter> documentBreakpoints = myDocumentBreakpoints.get(document);
+ final boolean reallyRemoved = documentBreakpoints.remove(breakpoint);
+ if (reallyRemoved) {
+ if (documentBreakpoints.isEmpty()) {
+ myDocumentBreakpoints.remove(document);
+ }
+ break;
+ }
+ }
+ }
+ //we delete breakpoints inside release, so gutter will not fire events to deleted breakpoints
+ breakpoint.delete();
+
+ myDispatcher.getMulticaster().breakpointsChanged();
+ }
+ }
+
+ @Override
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(@NotNull final Element parentNode) throws WriteExternalException {
+ WriteExternalException ex = ApplicationManager.getApplication().runReadAction(new Computable<WriteExternalException>() {
+ @Override
+ @Nullable
+ public WriteExternalException compute() {
+ try {
+ removeInvalidBreakpoints();
+ final Map<Key<? extends Breakpoint>, Element> categoryToElementMap = new java.util.HashMap<Key<? extends Breakpoint>, Element>();
+ for (Key<? extends Breakpoint> category : myBreakpointDefaults.keySet()) {
+ final Element group = getCategoryGroupElement(categoryToElementMap, category, parentNode);
+ final BreakpointDefaults defaults = getBreakpointDefaults(category);
+ group.setAttribute(DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME, String.valueOf(defaults.getSuspendPolicy()));
+ group.setAttribute(DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME, String.valueOf(defaults.isConditionEnabled()));
+ }
+ for (final Breakpoint breakpoint : getBreakpoints()) {
+ final Key<? extends Breakpoint> category = breakpoint.getCategory();
+ final Element group = getCategoryGroupElement(categoryToElementMap, category, parentNode);
+ if (breakpoint.isValid()) {
+ writeBreakpoint(group, breakpoint);
+ }
+ }
+ final AnyExceptionBreakpoint anyExceptionBreakpoint = getAnyExceptionBreakpoint();
+ final Element group = getCategoryGroupElement(categoryToElementMap, anyExceptionBreakpoint.getCategory(), parentNode);
+ writeBreakpoint(group, anyExceptionBreakpoint);
+
+ final Element rules = new Element(RULES_GROUP_NAME);
+ parentNode.addContent(rules);
+ for (final EnableBreakpointRule myBreakpointRule : myBreakpointRules) {
+ writeRule(myBreakpointRule, rules);
+ }
+
+ return null;
+ }
+ catch (WriteExternalException e) {
+ return e;
+ }
+ }
+ });
+ if (ex != null) {
+ throw ex;
+ }
+
+ final Element props = new Element("ui_properties");
+ parentNode.addContent(props);
+ for (final String name : myUIProperties.keySet()) {
+ final String value = myUIProperties.get(name);
+ final Element property = new Element("property");
+ props.addContent(property);
+ property.setAttribute("name", name);
+ property.setAttribute("value", value);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) private static void writeRule(@NotNull final EnableBreakpointRule enableBreakpointRule, @NotNull Element element) {
+ Element rule = new Element("rule");
+ if (enableBreakpointRule.isLeaveEnabled()) {
+ rule.setAttribute("leaveEnabled", Boolean.toString(true));
+ }
+ element.addContent(rule);
+ writeRuleBreakpoint(rule, MASTER_BREAKPOINT_TAGNAME, enableBreakpointRule.getMasterBreakpoint());
+ writeRuleBreakpoint(rule, SLAVE_BREAKPOINT_TAGNAME, enableBreakpointRule.getSlaveBreakpoint());
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) private static void writeRuleBreakpoint(@NotNull final Element element, final String tagName, @NotNull final Breakpoint breakpoint) {
+ Element master = new Element(tagName);
+ element.addContent(master);
+ master.setAttribute("name", breakpoint.getDisplayName());
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void writeBreakpoint(@NotNull final Element group, @NotNull final Breakpoint breakpoint) throws WriteExternalException {
+ Element breakpointNode = new Element("breakpoint");
+ group.addContent(breakpointNode);
+ breakpoint.writeExternal(breakpointNode);
+ }
+
+ private static <T extends Breakpoint> Element getCategoryGroupElement(@NotNull final Map<Key<? extends Breakpoint>, Element> categoryToElementMap, @NotNull final Key<T> category, @NotNull final Element parentNode) {
+ Element group = categoryToElementMap.get(category);
+ if (group == null) {
+ group = new Element(category.toString());
+ categoryToElementMap.put(category, group);
+ parentNode.addContent(group);
+ }
+ return group;
+ }
+
+ private void removeInvalidBreakpoints() {
+ ArrayList<Breakpoint> toDelete = new ArrayList<Breakpoint>();
+
+ for (Breakpoint breakpoint : getBreakpoints()) {
+ if (!breakpoint.isValid()) {
+ toDelete.add(breakpoint);
+ }
+ }
+
+ for (final Breakpoint aToDelete : toDelete) {
+ removeBreakpoint(aToDelete);
+ }
+ }
+
+ /**
+ * @return breakpoints of one of the category:
+ * LINE_BREAKPOINTS, EXCEPTION_BREKPOINTS, FIELD_BREAKPOINTS, METHOD_BREAKPOINTS
+ */
+ public <T extends Breakpoint> Breakpoint[] getBreakpoints(@NotNull final Key<T> category) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ removeInvalidBreakpoints();
+
+ final ArrayList<Breakpoint> breakpoints = new ArrayList<Breakpoint>();
+
+ for (Breakpoint breakpoint : getBreakpoints()) {
+ if (category.equals(breakpoint.getCategory())) {
+ breakpoints.add(breakpoint);
+ }
+ }
+
+ return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
+ }
+
+ @NotNull
+ public synchronized List<Breakpoint> getBreakpoints() {
+ if (myBreakpointsListForIteration == null) {
+ myBreakpointsListForIteration = new ArrayList<Breakpoint>(myBreakpoints.size() + 1);
+ myBreakpointsListForIteration.addAll(myBreakpoints);
+ myBreakpointsListForIteration.add(getAnyExceptionBreakpoint());
+ }
+ return myBreakpointsListForIteration;
+ }
+
+ public AnyExceptionBreakpoint getAnyExceptionBreakpoint() {
+ if (myAnyExceptionBreakpoint == null) {
+ myAnyExceptionBreakpoint = new AnyExceptionBreakpoint(myProject);
+ }
+ return myAnyExceptionBreakpoint;
+ }
+
+ //interaction with RequestManagerImpl
+ public void disableBreakpoints(@NotNull final DebugProcessImpl debugProcess) {
+ final List<Breakpoint> breakpoints = getBreakpoints();
+ if (!breakpoints.isEmpty()) {
+ final RequestManagerImpl requestManager = debugProcess.getRequestsManager();
+ for (Breakpoint breakpoint : breakpoints) {
+ breakpoint.markVerified(requestManager.isVerified(breakpoint));
+ requestManager.deleteRequest(breakpoint);
+ }
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ updateBreakpointsUI();
+ }
+ });
+ }
+ }
+
+ public void enableBreakpoints(final DebugProcessImpl debugProcess) {
+ final List<Breakpoint> breakpoints = getBreakpoints();
+ if (!breakpoints.isEmpty()) {
+ for (Breakpoint breakpoint : breakpoints) {
+ breakpoint.markVerified(false); // clean cached state
+ breakpoint.createRequest(debugProcess);
+ }
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ updateBreakpointsUI();
+ }
+ });
+ }
+ }
+
+ public void applyThreadFilter(@NotNull final DebugProcessImpl debugProcess, @Nullable ThreadReference newFilterThread) {
+ final RequestManagerImpl requestManager = debugProcess.getRequestsManager();
+ final ThreadReference oldFilterThread = requestManager.getFilterThread();
+ if (Comparing.equal(newFilterThread, oldFilterThread)) {
+ // the filter already added
+ return;
+ }
+ requestManager.setFilterThread(newFilterThread);
+ if (newFilterThread == null || oldFilterThread != null) {
+ final List<Breakpoint> breakpoints = getBreakpoints();
+ for (Breakpoint breakpoint : breakpoints) {
+ if (LineBreakpoint.CATEGORY.equals(breakpoint.getCategory()) || MethodBreakpoint.CATEGORY.equals(breakpoint.getCategory())) {
+ requestManager.deleteRequest(breakpoint);
+ breakpoint.createRequest(debugProcess);
+ }
+ }
+ }
+ else {
+ // important! need to add filter to _existing_ requests, otherwise Requestor->Request mapping will be lost
+ // and debugger trees will not be restored to original state
+ abstract class FilterSetter <T extends EventRequest> {
+ void applyFilter(@NotNull final List<T> requests, final ThreadReference thread) {
+ for (T request : requests) {
+ try {
+ final boolean wasEnabled = request.isEnabled();
+ if (wasEnabled) {
+ request.disable();
+ }
+ addFilter(request, thread);
+ if (wasEnabled) {
+ request.enable();
+ }
+ }
+ catch (InternalException e) {
+ LOG.info(e);
+ }
+ }
+ }
+ protected abstract void addFilter(final T request, final ThreadReference thread);
+ }
+
+ final EventRequestManager eventRequestManager = requestManager.getVMRequestManager();
+
+ new FilterSetter<BreakpointRequest>() {
+ @Override
+ protected void addFilter(@NotNull final BreakpointRequest request, final ThreadReference thread) {
+ request.addThreadFilter(thread);
+ }
+ }.applyFilter(eventRequestManager.breakpointRequests(), newFilterThread);
+
+ new FilterSetter<MethodEntryRequest>() {
+ @Override
+ protected void addFilter(@NotNull final MethodEntryRequest request, final ThreadReference thread) {
+ request.addThreadFilter(thread);
+ }
+ }.applyFilter(eventRequestManager.methodEntryRequests(), newFilterThread);
+
+ new FilterSetter<MethodExitRequest>() {
+ @Override
+ protected void addFilter(@NotNull final MethodExitRequest request, final ThreadReference thread) {
+ request.addThreadFilter(thread);
+ }
+ }.applyFilter(eventRequestManager.methodExitRequests(), newFilterThread);
+ }
+ }
+
+ public void updateBreakpoints(final DebugProcessImpl debugProcess) {
+ List<Breakpoint> breakpoints = getBreakpoints();
+ for (Breakpoint breakpoint : breakpoints) {
+ RequestManagerImpl.updateRequests(breakpoint);
+ }
+ }
+
+ public void updateAllRequests() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+
+ List<Breakpoint> breakpoints = getBreakpoints();
+ for (Breakpoint breakpoint : breakpoints) {
+ fireBreakpointChanged(breakpoint);
+ }
+ }
+
+ public void updateBreakpointsUI() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ for (Breakpoint breakpoint : getBreakpoints()) {
+ breakpoint.updateUI();
+ }
+ }
+
+ public void reloadBreakpoints() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+
+ for (Breakpoint breakpoint : getBreakpoints()) {
+ breakpoint.reload();
+ }
+ }
+
+ public void addBreakpointManagerListener(@NotNull BreakpointManagerListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ public void removeBreakpointManagerListener(@NotNull BreakpointManagerListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+
+ private boolean myAllowMulticasting = true;
+ private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+ public void fireBreakpointChanged(Breakpoint breakpoint) {
+ RequestManagerImpl.updateRequests(breakpoint);
+ if (myAllowMulticasting) {
+ // can be invoked from non-AWT thread
+ myAlarm.cancelAllRequests();
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ myAlarm.addRequest(new Runnable() {
+ @Override
+ public void run() {
+ myDispatcher.getMulticaster().breakpointsChanged();
+ }
+ }, 100);
+ }
+ };
+ if (ApplicationManager.getApplication().isDispatchThread()) {
+ runnable.run();
+ }
+ else {
+ SwingUtilities.invokeLater(runnable);
+ }
+ }
+ }
+
+ public void setBreakpointEnabled(@NotNull final Breakpoint breakpoint, final boolean enabled) {
+ if (breakpoint.ENABLED != enabled) {
+ breakpoint.ENABLED = enabled;
+ fireBreakpointChanged(breakpoint);
+ breakpoint.updateUI();
+ }
+ }
+
+ public void addBreakpointRule(@NotNull EnableBreakpointRule rule) {
+ rule.init();
+ myBreakpointRules.add(rule);
+ }
+
+ public boolean removeBreakpointRule(@NotNull EnableBreakpointRule rule) {
+ final boolean removed = myBreakpointRules.remove(rule);
+ if (removed) {
+ rule.dispose();
+ }
+ return removed;
+ }
+
+ public boolean removeBreakpointRule(@NotNull Breakpoint slaveBreakpoint) {
+ for (final EnableBreakpointRule rule : myBreakpointRules) {
+ if (slaveBreakpoint.equals(rule.getSlaveBreakpoint())) {
+ removeBreakpointRule(rule);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updateBreakpointRules(@NotNull Breakpoint removedBreakpoint) {
+ for (Iterator<EnableBreakpointRule> it = myBreakpointRules.iterator(); it.hasNext();) {
+ final EnableBreakpointRule rule = it.next();
+ if (removedBreakpoint.equals(rule.getMasterBreakpoint()) || removedBreakpoint.equals(rule.getSlaveBreakpoint())) {
+ it.remove();
+ }
+ }
+ }
+
+ public void processBreakpointHit(@NotNull final Breakpoint breakpoint) {
+ for (final EnableBreakpointRule rule : myBreakpointRules) {
+ rule.processBreakpointHit(breakpoint);
+ }
+ }
+
+ public void setInitialBreakpointsState() {
+ myAllowMulticasting = false;
+ for (final EnableBreakpointRule myBreakpointRule : myBreakpointRules) {
+ myBreakpointRule.init();
+ }
+ myAllowMulticasting = true;
+ if (!myBreakpointRules.isEmpty()) {
+ IJSwingUtilities.invoke(new Runnable() {
+ @Override
+ public void run() {
+ myDispatcher.getMulticaster().breakpointsChanged();
+ }
+ });
+ }
+ }
+
+ @Nullable
+ public Breakpoint findMasterBreakpoint(@NotNull Breakpoint dependentBreakpoint) {
+ for (final EnableBreakpointRule rule : myBreakpointRules) {
+ if (dependentBreakpoint.equals(rule.getSlaveBreakpoint())) {
+ return rule.getMasterBreakpoint();
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public EnableBreakpointRule findBreakpointRule(@NotNull Breakpoint dependentBreakpoint) {
+ for (final EnableBreakpointRule rule : myBreakpointRules) {
+ if (dependentBreakpoint.equals(rule.getSlaveBreakpoint())) {
+ return rule;
+ }
+ }
+ return null;
+ }
+
+ public String getProperty(String name) {
+ return myUIProperties.get(name);
+ }
+
+ public String setProperty(String name, String value) {
+ return myUIProperties.put(name, value);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManagerListener.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManagerListener.java
new file mode 100644
index 0000000..8d84aeb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManagerListener.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import java.util.EventListener;
+
+public interface BreakpointManagerListener extends EventListener{
+ void breakpointsChanged ();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointNameCellRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointNameCellRenderer.java
new file mode 100644
index 0000000..eac638e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointNameCellRenderer.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import javax.swing.table.DefaultTableCellRenderer;
+import java.awt.*;
+
+/**
+ * @author Jeka
+ */
+public class BreakpointNameCellRenderer extends DefaultTableCellRenderer {
+ private final Color myAnyExceptionForeground = new Color(128, 0, 0);
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ setBorder(null);
+ BreakpointTableModel tableModel = (BreakpointTableModel)table.getModel();
+ Breakpoint breakpoint = tableModel.getBreakpoint(row);
+ if (breakpoint == null){
+ return this;
+ };
+ final Icon icon = (breakpoint instanceof BreakpointWithHighlighter)?
+ breakpoint.ENABLED? ((BreakpointWithHighlighter)breakpoint).getSetIcon(false) : ((BreakpointWithHighlighter)breakpoint).getDisabledIcon(
+ false) : breakpoint.getIcon();
+ setIcon(icon);
+ setDisabledIcon(icon);
+
+ if(isSelected){
+ setForeground(UIUtil.getTableSelectionForeground());
+ }
+ else{
+ Color foreColor;
+ if(breakpoint instanceof AnyExceptionBreakpoint){
+ foreColor = myAnyExceptionForeground;
+ }
+ else{
+ foreColor = UIUtil.getTableForeground();
+ }
+ setForeground(foreColor);
+ }
+ setEnabled(isSelected || breakpoint.ENABLED);
+ return this;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPanel.form b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPanel.form
new file mode 100644
index 0000000..ff8bf76
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPanel.form
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.ui.breakpoints.BreakpointPanel">
+ <grid id="800c4" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="42" y="71" width="312" height="181"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="9a369" binding="myBreakPointsPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="7" vgap="-1">
+ <margin top="0" left="5" bottom="5" right="5"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <xy id="c9dc2" binding="myTablePlace" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </xy>
+ <xy id="73b7b" binding="myButtonsPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </xy>
+ </children>
+ </grid>
+ <xy id="96060" binding="myPropertiesPanelPlace" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="empty">
+ <size top="0" left="5" bottom="5" right="5"/>
+ </border>
+ <children/>
+ </xy>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPanel.java
new file mode 100644
index 0000000..a19c9e5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPanel.java
@@ -0,0 +1,559 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.ui.breakpoints.actions.BreakpointPanelAction;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiField;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.SideBorder;
+import com.intellij.ui.TableUtil;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.tree.TreeUtil;
+import com.intellij.xdebugger.impl.breakpoints.ui.AbstractBreakpointPanel;
+import gnu.trove.TIntArrayList;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Jeka
+ */
+public class BreakpointPanel extends AbstractBreakpointPanel<Breakpoint> {
+ private final BreakpointPropertiesPanel myPropertiesPanel;
+ private final BreakpointPanelAction[] myActions;
+ private final Key<? extends Breakpoint> myBreakpointCategory;
+ private Breakpoint myCurrentViewableBreakpoint;
+ private final List<Runnable> myDisposeActions = new ArrayList<Runnable>();
+
+ private final Project myProject;
+ private JPanel myPanel;
+ private JPanel myBreakPointsPanel;
+ private JPanel myTablePlace;
+ private JPanel myPropertiesPanelPlace;
+ private final BreakpointTable myTable;
+ private final BreakpointTree myTree;
+ private JPanel myButtonsPanel;
+ private String myCurrentViewId = TABLE_VIEW;
+
+ private static final @NonNls String PROPERTIES_STUB = "STUB";
+ private static final @NonNls String PROPERTIES_DATA = "DATA";
+
+ public static final @NonNls String TREE_VIEW = "TREE";
+ public static final @NonNls String TABLE_VIEW = "TABLE";
+
+ public BreakpointPanel(final Project project,
+ BreakpointPropertiesPanel propertiesPanel,
+ final BreakpointPanelAction[] actions,
+ Key<? extends Breakpoint> breakpointCategory,
+ String tabName,
+ String helpId) {
+ super(tabName, helpId, Breakpoint.class);
+ myProject = project;
+ myPropertiesPanel = propertiesPanel;
+ myActions = actions;
+ myBreakpointCategory = breakpointCategory;
+
+ myTable = new BreakpointTable(project);
+ myTree = new BreakpointTree(project);
+
+ myTablePlace.setLayout(new CardLayout());
+ JScrollPane pane = ScrollPaneFactory.createScrollPane(myTable);
+ pane.putClientProperty(UIUtil.KEEP_BORDER_SIDES, SideBorder.ALL);
+ myTablePlace.add(pane, TABLE_VIEW);
+
+ final ListSelectionListener listSelectionListener = new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) {
+ updateCurrentBreakpointPropertiesPanel();
+ }
+ }
+ };
+ final ListSelectionModel tableSelectionModel = myTable.getSelectionModel();
+ tableSelectionModel.addListSelectionListener(listSelectionListener);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ tableSelectionModel.removeListSelectionListener(listSelectionListener);
+ }
+ });
+
+ final TreeSelectionModel treeSelectionModel = myTree.getSelectionModel();
+ final TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
+ public void valueChanged(TreeSelectionEvent e) {
+ updateCurrentBreakpointPropertiesPanel();
+ }
+ };
+ treeSelectionModel.addTreeSelectionListener(treeSelectionListener);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ treeSelectionModel.removeTreeSelectionListener(treeSelectionListener);
+ }
+ });
+
+ final BreakpointTableModel tableModel = myTable.getModel();
+ final TableModelListener tableModelListener = new TableModelListener() {
+ public void tableChanged(TableModelEvent e) {
+ if (e.getType() == TableModelEvent.UPDATE) {
+ updateCurrentBreakpointPropertiesPanel();
+ }
+ fireBreakpointsChanged();
+ }
+ };
+ tableModel.addTableModelListener(tableModelListener);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ tableModel.removeTableModelListener(tableModelListener);
+ }
+ });
+
+
+ final TreeModel treeModel = myTree.getModel();
+ final TreeModelListener treeModelListener = new TreeModelListener() {
+ public void treeNodesChanged(TreeModelEvent e) {
+ }
+
+ public void treeNodesInserted(TreeModelEvent e) {
+ }
+
+ public void treeNodesRemoved(TreeModelEvent e) {
+ }
+
+ public void treeStructureChanged(TreeModelEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ensureSelectionExists();
+ updateButtons();
+ }
+ });
+ }
+ };
+ treeModel.addTreeModelListener(treeModelListener);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ treeModel.removeTreeModelListener(treeModelListener);
+ }
+ });
+
+ myPropertiesPanelPlace.setLayout(new CardLayout());
+ final JPanel stubPanel = new JPanel();
+ stubPanel.setMinimumSize(myPropertiesPanel.getPanel().getMinimumSize());
+ myPropertiesPanelPlace.add(stubPanel, PROPERTIES_STUB);
+ myPropertiesPanelPlace.add(myPropertiesPanel.getPanel(), PROPERTIES_DATA);
+
+ myBreakPointsPanel.setBorder(IdeBorderFactory.createEmptyBorder(6, 6, 0, 6));
+
+ myButtonsPanel.setLayout(new GridBagLayout());
+ for (int idx = 0; idx < actions.length; idx++) {
+ final BreakpointPanelAction action = actions[idx];
+ action.setPanel(this);
+ final AbstractButton button = action.isStateAction()? new JCheckBox(action.getName()) : new JButton(action.getName());
+ action.setButton(button);
+ button.addActionListener(action);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ button.removeActionListener(action);
+ }
+ });
+ final double weighty = (idx == actions.length - 1) ? 1.0 : 0.0;
+ myButtonsPanel.add(button, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, weighty, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 2, 2, 2), 0, 0));
+ }
+ final ListSelectionListener tableSelectionListener = new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ updateButtons();
+ }
+ };
+ tableSelectionModel.addListSelectionListener(tableSelectionListener);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ tableSelectionModel.removeListSelectionListener(tableSelectionListener);
+ }
+ });
+
+ pane = ScrollPaneFactory.createScrollPane(myTree);
+ pane.putClientProperty(UIUtil.KEEP_BORDER_SIDES, SideBorder.ALL);
+ myTablePlace.add(pane, TREE_VIEW);
+
+ updateCurrentBreakpointPropertiesPanel();
+
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ final BreakpointManagerListener breakpointManagerListener = new BreakpointManagerListener() {
+ public void breakpointsChanged() {
+ if (isTreeShowing()) {
+ myTree.repaint();
+ }
+ else {
+ myTable.repaint();
+ }
+ }
+ };
+ breakpointManager.addBreakpointManagerListener(breakpointManagerListener);
+ myDisposeActions.add(new Runnable() {
+ public void run() {
+ breakpointManager.removeBreakpointManagerListener(breakpointManagerListener);
+ }
+ });
+ }
+
+ public BreakpointTable getTable() {
+ return myTable;
+ }
+
+ public BreakpointTree getTree() {
+ return myTree;
+ }
+
+ public void switchViews() {
+ final Breakpoint[] selectedBreakpoints = getSelectedBreakpoints();
+ showView(isTreeShowing() ? TABLE_VIEW : TREE_VIEW);
+ selectBreakpoints(selectedBreakpoints);
+ }
+
+ public void showView(final String viewId) {
+ if (TREE_VIEW.equals(viewId) || TABLE_VIEW.equals(viewId)) {
+ myCurrentViewId = viewId;
+ ((CardLayout)myTablePlace.getLayout()).show(myTablePlace, viewId);
+ updateButtons();
+ ensureSelectionExists();
+ }
+ }
+
+ public String getCurrentViewId() {
+ return myCurrentViewId;
+ }
+
+ public boolean isTreeShowing() {
+ return BreakpointPanel.TREE_VIEW.equals(getCurrentViewId());
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void setupPanelUI() {
+ final BreakpointManager breakpointManager = getBreakpointManager();
+ final Key<? extends Breakpoint> category = getBreakpointCategory();
+ final BreakpointTree tree = getTree();
+ final String flattenPackages = breakpointManager.getProperty(category + "_flattenPackages");
+ if (flattenPackages != null) {
+ tree.setFlattenPackages("true".equalsIgnoreCase(flattenPackages));
+ }
+ final String groupByClasses = breakpointManager.getProperty(category + "_groupByClasses");
+ if (groupByClasses != null) {
+ tree.setGroupByClasses("true".equalsIgnoreCase(groupByClasses));
+ }
+ final String groupByMethods = breakpointManager.getProperty(category + "_groupByMethods");
+ if (groupByMethods != null) {
+ tree.setGroupByMethods("true".equalsIgnoreCase(groupByMethods));
+ }
+
+ final String viewId = breakpointManager.getProperty(category + "_viewId");
+ if (viewId != null) {
+ showView(viewId);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void savePanelSettings() {
+ Key<? extends Breakpoint> category = getBreakpointCategory();
+ final BreakpointManager breakpointManager = getBreakpointManager();
+
+ final BreakpointTree tree = getTree();
+ breakpointManager.setProperty(category + "_flattenPackages", tree.isFlattenPackages() ? "true" : "false");
+ breakpointManager.setProperty(category + "_groupByClasses", tree.isGroupByClasses() ? "true" : "false");
+ breakpointManager.setProperty(category + "_groupByMethods", tree.isGroupByMethods() ? "true" : "false");
+ breakpointManager.setProperty(category + "_viewId", getCurrentViewId());
+ }
+
+ protected BreakpointManager getBreakpointManager() {
+ return DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+ }
+
+ private boolean hasEnabledBreakpoints() {
+ final List<Breakpoint> breakpoints = getBreakpoints();
+ for (Breakpoint breakpoint : breakpoints) {
+ if (breakpoint.ENABLED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Icon getTabIcon() {
+ final BreakpointFactory factory = BreakpointFactory.getInstance(getBreakpointCategory());
+ return hasEnabledBreakpoints() ? factory.getIcon() : factory.getDisabledIcon();
+ }
+
+ public boolean canSelectBreakpoint(final Breakpoint breakpoint) {
+ return breakpoint.getCategory().equals(getBreakpointCategory());
+ }
+
+ public Key<? extends Breakpoint> getBreakpointCategory() {
+ return myBreakpointCategory;
+ }
+
+ public Breakpoint getCurrentViewableBreakpoint() {
+ return myCurrentViewableBreakpoint;
+ }
+
+ public void saveBreakpoints() {
+ if (myCurrentViewableBreakpoint != null) {
+ myPropertiesPanel.saveTo(myCurrentViewableBreakpoint, new Runnable() {
+ public void run() {
+ myTable.repaint();
+ }
+ });
+ }
+ }
+
+ public void updateButtons() {
+ for (final BreakpointPanelAction action : myActions) {
+ final AbstractButton button = action.getButton();
+ action.update();
+ if (!button.isEnabled() && button.hasFocus()) {
+ button.transferFocus();
+ }
+ }
+ }
+
+ public JPanel getPanel() {
+ return myPanel;
+ }
+
+ public void selectBreakpoint(Breakpoint breakpoint) {
+ if (isTreeShowing()) {
+ myTree.selectBreakpoint(breakpoint);
+ }
+ else {
+ int index = myTable.getModel().getBreakpointIndex(breakpoint);
+ ListSelectionModel model = myTable.getSelectionModel();
+ model.clearSelection();
+ model.addSelectionInterval(index, index);
+ }
+ }
+
+ @Override
+ public boolean hasBreakpoints() {
+ return getBreakpointManager().getBreakpoints(getBreakpointCategory()).length > 0;
+ }
+
+ public void selectBreakpoints(Breakpoint[] breakpoints) {
+ if (isTreeShowing()) {
+ myTree.selectBreakpoints(breakpoints);
+ }
+ else {
+ final TIntArrayList rows = new TIntArrayList(breakpoints.length);
+ for (Breakpoint breakpoint : breakpoints) {
+ final int index = myTable.getModel().getBreakpointIndex(breakpoint);
+ if (index >= 0) {
+ rows.add(index);
+ }
+ }
+ myTable.getSelectionModel().clearSelection();
+ TableUtil.selectRows(myTable, rows.toNativeArray());
+ }
+ }
+
+ public void resetBreakpoints() {
+ Breakpoint[] breakpoints = getBreakpointManager().getBreakpoints(getBreakpointCategory());
+ myTable.setBreakpoints(breakpoints);
+ myTree.setBreakpoints(breakpoints);
+ ensureSelectionExists();
+ updateButtons();
+ }
+
+ public Breakpoint[] getSelectedBreakpoints() {
+ return isTreeShowing() ? myTree.getSelectedBreakpoints() : myTable.getSelectedBreakpoints();
+ }
+
+ public void removeSelectedBreakpoints() {
+ final Breakpoint[] selectedBreakpoints = getSelectedBreakpoints();
+ if (selectedBreakpoints.length == 0) {
+ return;
+ }
+
+ final boolean inTreeMode = isTreeShowing();
+
+ final int minSelectionIndex;
+ if (inTreeMode) {
+ minSelectionIndex = Math.max(0, myTree.getSelectionModel().getMinSelectionRow());
+ }
+ else {
+ minSelectionIndex = Math.max(0, myTable.getSelectionModel().getMinSelectionIndex());
+ }
+
+ myTree.removeBreakpoints(selectedBreakpoints);
+ myTable.getModel().removeBreakpoints(selectedBreakpoints);
+ myCurrentViewableBreakpoint = null;
+
+ if (inTreeMode) {
+ if (myTree.getRowCount() > 0) {
+ int rowToSelect = minSelectionIndex >= myTree.getRowCount()? myTree.getRowCount() - 1 : minSelectionIndex;
+ final TreePath path = myTree.getPathForRow(rowToSelect);
+ if (path != null) {
+ TreeUtil.selectPath(myTree, path, true);
+ }
+ }
+ }
+ else {
+ if (myTable.getRowCount() > 0) { // if in table mode and there are items to select
+ int indexToSelect = minSelectionIndex >= myTable.getRowCount()? myTable.getRowCount() - 1 : minSelectionIndex;
+ TableUtil.selectRows(myTable, new int[] {indexToSelect});
+ }
+ }
+
+ updateCurrentBreakpointPropertiesPanel();
+ }
+
+ public void insertBreakpointAt(Breakpoint breakpoint, int index) {
+ myTable.getModel().insertBreakpointAt(breakpoint, index);
+ myTree.addBreakpoint(breakpoint);
+ selectBreakpoint(breakpoint);
+ }
+
+ public void addBreakpoint(Breakpoint breakpoint) {
+ myTable.getModel().addBreakpoint(breakpoint);
+ myTree.addBreakpoint(breakpoint);
+ selectBreakpoint(breakpoint);
+ }
+
+ private void updateCurrentBreakpointPropertiesPanel() {
+ if (myCurrentViewableBreakpoint != null) {
+ myPropertiesPanel.saveTo(myCurrentViewableBreakpoint, new Runnable() {
+ public void run() {
+ if (isTreeShowing()) {
+ myTree.repaint();
+ }
+ else {
+ myTable.repaint();
+ }
+ }
+ });
+ }
+ Breakpoint[] breakpoints = getSelectedBreakpoints();
+ Breakpoint oldViewableBreakpoint = myCurrentViewableBreakpoint;
+ myCurrentViewableBreakpoint = (breakpoints != null && breakpoints.length == 1) ? breakpoints[0] : null;
+ if (myCurrentViewableBreakpoint != null) {
+ if (oldViewableBreakpoint == null) {
+ ((CardLayout)myPropertiesPanelPlace.getLayout()).show(myPropertiesPanelPlace, PROPERTIES_DATA);
+ }
+ myPropertiesPanel.initFrom(myCurrentViewableBreakpoint, true);
+ }
+ else {
+ ((CardLayout)myPropertiesPanelPlace.getLayout()).show(myPropertiesPanelPlace, PROPERTIES_STUB);
+ }
+ updateButtons();
+ }
+
+ public JComponent getControl(String control) {
+ return myPropertiesPanel.getControl(control);
+ }
+
+ public int getBreakpointCount() {
+ return myTable.getBreakpoints().size();
+ }
+
+ public final List<Breakpoint> getBreakpoints() {
+ return myTable.getBreakpoints();
+ }
+
+ public void dispose() {
+ savePanelSettings();
+ for (Runnable runnable : myDisposeActions) {
+ runnable.run();
+ }
+ myDisposeActions.clear();
+
+ myTree.dispose();
+
+ final KeyStroke[] tableStrokes = myTable.getRegisteredKeyStrokes();
+ for (KeyStroke stroke : tableStrokes) {
+ myTable.unregisterKeyboardAction(stroke);
+ }
+
+ myPropertiesPanel.dispose();
+ }
+
+ public OpenFileDescriptor createEditSourceDescriptor(final Project project) {
+ Breakpoint[] breakpoints = getSelectedBreakpoints();
+ if (breakpoints == null || breakpoints.length == 0) {
+ return null;
+ }
+ Breakpoint br = breakpoints[0];
+ int line;
+ Document doc;
+ if (br instanceof BreakpointWithHighlighter) {
+ BreakpointWithHighlighter breakpoint = (BreakpointWithHighlighter)br;
+ doc = breakpoint.getDocument();
+ line = breakpoint.getLineIndex();
+ }
+ else {
+ return null;
+ }
+ if (line < 0 || line >= doc.getLineCount()) {
+ return null;
+ }
+ int offset = doc.getLineStartOffset(line);
+ if(br instanceof FieldBreakpoint) {
+ PsiField field = ((FieldBreakpoint) br).getPsiField();
+ if(field != null) {
+ offset = field.getTextOffset();
+ }
+ }
+ VirtualFile vFile = FileDocumentManager.getInstance().getFile(doc);
+ if (vFile == null || !vFile.isValid()) {
+ return null;
+ }
+ return new OpenFileDescriptor(project, vFile, offset);
+ }
+
+ public void ensureSelectionExists() {
+ if (myTable.getRowCount() > 0 && myTable.getSelectedRow() < 0) {
+ ListSelectionModel model = myTable.getSelectionModel();
+ model.clearSelection();
+ model.addSelectionInterval(0, 0);
+ }
+
+ final List<Breakpoint> treeBreakpoints = myTree.getBreakpoints();
+ if (treeBreakpoints.size() > 0) {
+ if (myTree.getSelectionModel().getSelectionCount() == 0) {
+ myTree.selectFirstBreakpoint();
+ }
+ }
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ if (isTreeShowing()) {
+ myTree.requestFocus();
+ }
+ else {
+ myTable.requestFocus();
+ }
+ }
+ });
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.form b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.form
new file mode 100644
index 0000000..7d71e21
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.form
@@ -0,0 +1,425 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.ui.breakpoints.BreakpointPropertiesPanel">
+ <grid id="6570" binding="myPanel" custom-create="true" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="23" y="37" width="751" height="404"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="7978d" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <xy id="f3049" binding="myConditionComboPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </xy>
+ <component id="f3923" class="com.intellij.ui.components.JBCheckBox" binding="myConditionCheckbox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Condition"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="763d0" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/>
+ </clientProperties>
+ <border type="none">
+ <font/>
+ </border>
+ <children>
+ <component id="f6192" class="javax.swing.JRadioButton" binding="myRbSuspendThread">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.suspend.thread"/>
+ </properties>
+ </component>
+ <component id="106f5" class="javax.swing.JRadioButton" binding="myRbSuspendAll">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.suspend.all"/>
+ </properties>
+ </component>
+ <hspacer id="46ea8">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <component id="be00d" class="javax.swing.JButton" binding="myMakeDefaultButton">
+ <constraints>
+ <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.suspend.default"/>
+ </properties>
+ </component>
+ <component id="cd928" class="com.intellij.ui.components.JBCheckBox" binding="myCbSuspend">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Suspend"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="5aab2" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="da86f" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="245" height="251"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="1d1c" binding="myActionsPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="5"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/>
+ </clientProperties>
+ <border type="etched" title-resource-bundle="messages/DebuggerBundle" title-key="label.breakpoint.properties.panel.group.actions"/>
+ <children>
+ <component id="185b0" class="javax.swing.JCheckBox" binding="myLogMessageCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.log.message"/>
+ </properties>
+ </component>
+ <grid id="42168" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="4a38" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <hspacer id="9c54c">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="15" height="-1"/>
+ </grid>
+ </constraints>
+ </hspacer>
+ <grid id="80950" binding="myLogExpressionComboPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ <component id="7b0ce" class="javax.swing.JCheckBox" binding="myLogExpressionCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.log.expression"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <xy id="862d9" binding="mySpecialBoxPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <visible value="true"/>
+ </properties>
+ <border type="none"/>
+ <children/>
+ </xy>
+ <grid id="d7b0a" binding="myDependsOnPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value=""/>
+ </clientProperties>
+ <border type="empty"/>
+ <children>
+ <grid id="4241e" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="5"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="9b21c" binding="myDependentBreakpointComboPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="15" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <component id="7f1f0" class="javax.swing.JLabel" binding="myEnableOrDisableLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.dependency.type.lable"/>
+ </properties>
+ </component>
+ <component id="e3baf" class="javax.swing.JRadioButton" binding="myDisableAgainRadio" default-binding="true">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.depends.disable.again"/>
+ </properties>
+ </component>
+ <component id="10e41" class="javax.swing.JRadioButton" binding="myLeaveEnabledRadioButton" default-binding="true">
+ <constraints>
+ <grid row="2" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.depends.leave.enabled"/>
+ </properties>
+ </component>
+ <component id="f3672" class="com.intellij.ui.components.JBLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/XDebuggerBundle" key="xbreakpoint.master.breakpoint.description"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <vspacer id="21184">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ <grid id="d03f5" binding="myConditionsPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="7">
+ <margin top="2" left="2" bottom="5" right="5"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/>
+ </clientProperties>
+ <border type="etched" title-resource-bundle="messages/DebuggerBundle" title-key="label.breakpoint.properties.panel.group.conditions"/>
+ <children>
+ <grid id="b404a" binding="myInstanceFiltersPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="20b41" class="javax.swing.JCheckBox" binding="myInstanceFiltersCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <margin top="2" left="2" bottom="0" right="2"/>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.instance.filters"/>
+ </properties>
+ </component>
+ <grid id="d9be2" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <hspacer id="e6448">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="15" height="-1"/>
+ </grid>
+ </constraints>
+ </hspacer>
+ <xy id="1114b" binding="myInstanceFiltersFieldPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </xy>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="f07fc" binding="myClassFiltersPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="40e6b" class="javax.swing.JCheckBox" binding="myClassFiltersCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <margin top="2" left="2" bottom="0" right="2"/>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.class.filters"/>
+ </properties>
+ </component>
+ <grid id="fbeb4" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <xy id="72d13" binding="myClassFiltersFieldPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </xy>
+ <hspacer id="1d45">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="15" height="-1"/>
+ </grid>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="21edd" binding="myPassCountPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="11e5a" class="javax.swing.JCheckBox" binding="myPassCountCheckbox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <margin top="2" left="2" bottom="0" right="2"/>
+ <text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.pass.count"/>
+ </properties>
+ </component>
+ <grid id="e8cc5" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="64df8" class="javax.swing.JTextField" binding="myPassCountField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="15" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <enabled value="true"/>
+ <horizontalAlignment value="10"/>
+ </properties>
+ </component>
+ <hspacer id="9be08">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="15" height="-1"/>
+ </grid>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <vspacer id="905e2">
+ <constraints>
+ <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <vspacer id="62b02">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ <buttonGroups>
+ <group name="buttonGroup1">
+ <member id="e3baf"/>
+ <member id="10e41"/>
+ </group>
+ </buttonGroups>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java
new file mode 100644
index 0000000..78abfe4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java
@@ -0,0 +1,858 @@
+/*
+ * Copyright 2000-2012 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 BreakpointPropertiesPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.InstanceFilter;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.CompletionEditor;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.debugger.ui.DebuggerStatementEditor;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.ide.util.ClassFilter;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.editor.event.DocumentListener;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.FixedSizeButton;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.FieldPanel;
+import com.intellij.ui.MultiLineTooltipUI;
+import com.intellij.ui.components.JBCheckBox;
+import com.intellij.ui.popup.util.DetailView;
+import com.intellij.util.IJSwingUtilities;
+import com.intellij.xdebugger.impl.DebuggerSupport;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointChooser;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointNoneItem;
+import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class BreakpointPropertiesPanel {
+
+ private BreakpointChooser myMasterBreakpointChooser;
+
+ public void setDetailView(DetailView detailView) {
+ myDetailView = detailView;
+ myMasterBreakpointChooser.setDetailView(detailView);
+ }
+
+ private DetailView myDetailView;
+
+ protected final Project myProject;
+ private final Key<? extends Breakpoint> myBreakpointCategory;
+ private boolean myCompact;
+ private JPanel myPanel;
+ private final DebuggerExpressionComboBox myConditionCombo;
+ private final DebuggerExpressionComboBox myLogExpressionCombo;
+ private JTextField myPassCountField;
+ private final FieldPanel myInstanceFiltersField;
+
+ private final FieldPanel myClassFiltersField;
+ private com.intellij.ui.classFilter.ClassFilter[] myClassFilters;
+ private com.intellij.ui.classFilter.ClassFilter[] myClassExclusionFilters;
+ private InstanceFilter[] myInstanceFilters;
+
+ private JCheckBox myLogExpressionCheckBox;
+ private JCheckBox myLogMessageCheckBox;
+ protected JCheckBox myPassCountCheckbox;
+ private JCheckBox myInstanceFiltersCheckBox;
+ private JCheckBox myClassFiltersCheckBox;
+
+ private JPanel myInstanceFiltersFieldPanel;
+ private JPanel myClassFiltersFieldPanel;
+ private JPanel myConditionComboPanel;
+ private JPanel myLogExpressionComboPanel;
+ private JPanel myDependentBreakpointComboPanel;
+ private JPanel mySpecialBoxPanel;
+ private PsiClass myBreakpointPsiClass;
+
+ private JRadioButton myRbSuspendThread;
+ private JRadioButton myRbSuspendAll;
+ private JBCheckBox myCbSuspend;
+ private JButton myMakeDefaultButton;
+
+ private JRadioButton myDisableAgainRadio;
+ private JRadioButton myLeaveEnabledRadioButton;
+
+ private JLabel myEnableOrDisableLabel;
+ private JPanel myDependsOnPanel;
+ private JPanel myInstanceFiltersPanel;
+ private JPanel myClassFiltersPanel;
+ private JPanel myPassCountPanel;
+ private JPanel myConditionsPanel;
+ private JPanel myActionsPanel;
+ private JBCheckBox myConditionCheckbox;
+
+ ButtonGroup mySuspendPolicyGroup;
+ public static final String CONTROL_LOG_MESSAGE = "logMessage";
+ private final FixedSizeButton myConditionMagnifierButton;
+ private boolean myMoreOptionsVisible = true;
+ private Breakpoint myBreakpoint;
+
+ public boolean isSaveOnRemove() {
+ return mySaveOnRemove;
+ }
+
+ public void setSaveOnRemove(boolean saveOnRemove) {
+ mySaveOnRemove = saveOnRemove;
+ }
+
+ private boolean mySaveOnRemove = false;
+
+ public boolean isMoreOptionsVisible() {
+ return myMoreOptionsVisible;
+ }
+
+ private void createUIComponents() {
+ myPanel = new JPanel() {
+ @Override
+ public void removeNotify() {
+ super.removeNotify();
+ if (mySaveOnRemove) {
+ saveTo(myBreakpoint, new Runnable() {
+ @Override
+ public void run() {}
+ });
+ }
+ }
+ };
+ }
+
+ public interface Delegate {
+
+ void showActionsPanel();
+ }
+ private Delegate myDelegate;
+
+ public JComponent getControl(String control) {
+ if(CONTROL_LOG_MESSAGE.equals(control)) {
+ return myLogExpressionCombo;
+ }
+ return null;
+ }
+
+ public void dispose() {
+ if (myConditionCombo != null) {
+ myConditionCombo.dispose();
+ }
+ if (myLogExpressionCombo != null) {
+ myLogExpressionCombo.dispose();
+ }
+ }
+
+ public void setActionsPanelVisible(boolean b) {
+ myActionsPanel.setVisible(b);
+ }
+
+ public void setMoreOptionsVisible(boolean b) {
+ myMoreOptionsVisible = b;
+ myDependsOnPanel.setVisible(b);
+ myConditionsPanel.setVisible(b);
+ if (b) {
+ myActionsPanel.setVisible(true);
+ }
+ if (!b) {
+ myPanel.setPreferredSize(new Dimension(500, -1));
+ }
+ }
+
+ public void setDelegate(Delegate delegate) {
+ myDelegate = delegate;
+ }
+
+ private class MyTextField extends JTextField {
+ public MyTextField() {
+ }
+
+ public String getToolTipText(MouseEvent event) {
+ reloadClassFilters();
+ updateClassFilterEditor(false);
+ reloadInstanceFilters();
+ updateInstanceFilterEditor(false);
+ String toolTipText = super.getToolTipText(event);
+ return getToolTipText().length() == 0 ? null : toolTipText;
+ }
+
+ public JToolTip createToolTip() {
+ JToolTip toolTip = new JToolTip(){{
+ setUI(new MultiLineTooltipUI());
+ }};
+ toolTip.setComponent(this);
+ return toolTip;
+ }
+ }
+
+ private static void insert(JPanel panel, JComponent component) {
+ panel.setLayout(new BorderLayout());
+ panel.add(component, BorderLayout.CENTER);
+ }
+
+ public BreakpointPropertiesPanel(final Project project, final Key<? extends Breakpoint> breakpointCategory, boolean compact) {
+ myProject = project;
+ myBreakpointCategory = breakpointCategory;
+ myCompact = compact;
+
+ mySuspendPolicyGroup = new ButtonGroup();
+ mySuspendPolicyGroup.add(myRbSuspendAll);
+ mySuspendPolicyGroup.add(myRbSuspendThread);
+
+ updateSuspendPolicyRbFont();
+ final ItemListener suspendPolicyChangeListener = new ItemListener() {
+ public void itemStateChanged(final ItemEvent e) {
+ final BreakpointDefaults defaults = getBreakpointManager(myProject).getBreakpointDefaults(breakpointCategory);
+ myMakeDefaultButton.setEnabled(!defaults.getSuspendPolicy().equals(getSelectedSuspendPolicy()) || defaults.isConditionEnabled() != myConditionCheckbox.isSelected());
+ }
+ };
+
+ myCbSuspend.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ final boolean enabled = myCbSuspend.isSelected();
+ myRbSuspendAll.setEnabled(enabled);
+ myRbSuspendThread.setEnabled(enabled);
+ }
+ });
+
+
+ myRbSuspendAll.addItemListener(suspendPolicyChangeListener);
+ myRbSuspendThread.addItemListener(suspendPolicyChangeListener);
+ myConditionCheckbox.addItemListener(suspendPolicyChangeListener);
+
+ myMakeDefaultButton.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent e) {
+ final BreakpointManager breakpointManager = getBreakpointManager(myProject);
+ final String suspendPolicy = getSelectedSuspendPolicy();
+ breakpointManager.setBreakpointDefaults(breakpointCategory, new BreakpointDefaults(suspendPolicy, myConditionCheckbox.isSelected()));
+ updateSuspendPolicyRbFont();
+ if (DebuggerSettings.SUSPEND_THREAD.equals(suspendPolicy)) {
+ myRbSuspendThread.requestFocus();
+ }
+ else {
+ myRbSuspendAll.requestFocus();
+ }
+ myMakeDefaultButton.setEnabled(false);
+ }
+ });
+
+ myConditionCombo = new DebuggerExpressionComboBox(project, "LineBreakpoint condition");
+ myConditionCombo.addDocumentListener(new DocumentListener() {
+ @Override
+ public void beforeDocumentChange(DocumentEvent event) {
+ myConditionCheckbox.setSelected(true);
+ }
+
+ @Override
+ public void documentChanged(DocumentEvent event) {
+
+ }
+ });
+
+ myConditionCombo.getEditorComponent().addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent event) {
+ myConditionCombo.setEnabled(true);
+ myConditionCheckbox.setSelected(true);
+ }
+ });
+
+ if (myCompact) {
+ myPanel.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent event) {
+ IdeFocusManager.findInstance().requestFocus(myConditionCombo, true);
+ }
+ });
+ }
+
+ myLogExpressionCombo = new DebuggerExpressionComboBox(project, "LineBreakpoint logMessage");
+
+ myInstanceFiltersField = new FieldPanel(new MyTextField(), "", null,
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ reloadInstanceFilters();
+ EditInstanceFiltersDialog _dialog = new EditInstanceFiltersDialog(myProject);
+ _dialog.setFilters(myInstanceFilters);
+ _dialog.show();
+ if(_dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
+ myInstanceFilters = _dialog.getFilters();
+ updateInstanceFilterEditor(true);
+ }
+ }
+ },
+ null
+ );
+
+ myClassFiltersField = new FieldPanel(new MyTextField(), "", null,
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ reloadClassFilters();
+
+ ClassFilter classFilter = createClassConditionFilter();
+
+ EditClassFiltersDialog _dialog = new EditClassFiltersDialog(myProject, classFilter);
+ _dialog.setFilters(myClassFilters, myClassExclusionFilters);
+ _dialog.show();
+ if (_dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
+ myClassFilters = _dialog.getFilters();
+ myClassExclusionFilters = _dialog.getExclusionFilters();
+ updateClassFilterEditor(true);
+ }
+ }
+ },
+ null
+ );
+ ToolTipManager.sharedInstance().registerComponent(myClassFiltersField.getTextField());
+ ToolTipManager.sharedInstance().registerComponent(myInstanceFiltersField.getTextField());
+
+ JComponent specialBox = createSpecialBox();
+ if(specialBox != null) {
+ insert(mySpecialBoxPanel, specialBox);
+ }
+ else {
+ mySpecialBoxPanel.setVisible(false);
+ }
+
+ final JPanel conditionPanel = new JPanel(new BorderLayout());
+ conditionPanel.add(myConditionCombo, BorderLayout.CENTER);
+ myConditionMagnifierButton = new FixedSizeButton(myConditionCombo);
+ conditionPanel.add(myConditionMagnifierButton, BorderLayout.EAST);
+ myConditionMagnifierButton.setFocusable(false);
+ myConditionMagnifierButton.addActionListener(new MagnifierButtonAction(project, myConditionCombo, "Condition"));
+
+ insert(myConditionComboPanel, conditionPanel);
+ insert(myLogExpressionComboPanel, myLogExpressionCombo);
+
+ insert(myInstanceFiltersFieldPanel, myInstanceFiltersField);
+ insert(myClassFiltersFieldPanel, myClassFiltersField);
+
+ DebuggerUIUtil.enableEditorOnCheck(myLogExpressionCheckBox, myLogExpressionCombo);
+ ActionListener updateListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateCheckboxes();
+ }
+ };
+ myPassCountCheckbox.addActionListener(updateListener);
+ myInstanceFiltersCheckBox.addActionListener(updateListener);
+ myClassFiltersCheckBox.addActionListener(updateListener);
+ myConditionCheckbox.addActionListener(updateListener);
+ DebuggerUIUtil.focusEditorOnCheck(myPassCountCheckbox, myPassCountField);
+ DebuggerUIUtil.focusEditorOnCheck(myLogExpressionCheckBox, myLogExpressionCombo);
+ DebuggerUIUtil.focusEditorOnCheck(myInstanceFiltersCheckBox, myInstanceFiltersField.getTextField());
+ DebuggerUIUtil.focusEditorOnCheck(myClassFiltersCheckBox, myClassFiltersField.getTextField());
+ DebuggerUIUtil.focusEditorOnCheck(myConditionCheckbox, myConditionCombo);
+
+ IJSwingUtilities.adjustComponentsOnMac(myCbSuspend);
+ IJSwingUtilities.adjustComponentsOnMac(myLogExpressionCheckBox);
+ IJSwingUtilities.adjustComponentsOnMac(myLogMessageCheckBox);
+ }
+
+ private List<BreakpointItem> getBreakpointItemsExceptMy() {
+ List<BreakpointItem> items = new ArrayList<BreakpointItem>();
+ final DebuggerSupport support = DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class);
+ support.getBreakpointPanelProvider().provideBreakpointItems(myProject, items);
+ for (BreakpointItem item : items) {
+ if (item.getBreakpoint() == myBreakpoint) {
+ items.remove(item);
+ break;
+ }
+ }
+ items.add(new BreakpointNoneItem());
+ return items;
+ }
+
+ private void saveMasterBreakpoint() {
+ Breakpoint masterBreakpoint = (Breakpoint)myMasterBreakpointChooser.getSelectedBreakpoint();
+ if (masterBreakpoint == null) {
+ getBreakpointManager(myProject).removeBreakpointRule(myBreakpoint);
+ }
+ else {
+ EnableBreakpointRule rule = findMasterBreakpointRule();
+ boolean selected = myLeaveEnabledRadioButton.isSelected();
+ if (rule != null) {
+ if (rule.getMasterBreakpoint() != masterBreakpoint || rule.isLeaveEnabled() != selected) {
+ getBreakpointManager(myProject).removeBreakpointRule(rule);
+ }
+ else {
+ return;
+ }
+ }
+ getBreakpointManager(myProject).addBreakpointRule(new EnableBreakpointRule(getBreakpointManager(myProject), masterBreakpoint, myBreakpoint, selected));
+ }
+ }
+
+ private String getSelectedSuspendPolicy() {
+ if (myRbSuspendThread.isSelected()) {
+ return DebuggerSettings.SUSPEND_THREAD;
+ }
+ return DebuggerSettings.SUSPEND_ALL;
+ }
+
+ private void updateSuspendPolicyRbFont() {
+ final String defPolicy = getBreakpointManager(myProject).getBreakpointDefaults(myBreakpointCategory).getSuspendPolicy();
+
+ final Font font = myRbSuspendAll.getFont().deriveFont(Font.PLAIN);
+ final Font boldFont = font.deriveFont(Font.BOLD);
+
+ myRbSuspendAll.setFont(DebuggerSettings.SUSPEND_ALL.equals(defPolicy)? boldFont : font);
+ myRbSuspendThread.setFont(DebuggerSettings.SUSPEND_THREAD.equals(defPolicy)? boldFont : font);
+ }
+
+ protected ClassFilter createClassConditionFilter() {
+ ClassFilter classFilter;
+ if(myBreakpointPsiClass != null) {
+ classFilter = new ClassFilter() {
+ public boolean isAccepted(PsiClass aClass) {
+ return myBreakpointPsiClass == aClass || aClass.isInheritor(myBreakpointPsiClass, true);
+ }
+ };
+ }
+ else {
+ classFilter = null;
+ }
+ return classFilter;
+ }
+
+ protected JComponent createSpecialBox() {
+ return null;
+ }
+
+ /**
+ * Init UI components with the values from Breakpoint
+ */
+ public void initFrom(Breakpoint breakpoint, boolean moreOptionsVisible1) {
+ myBreakpoint = breakpoint;
+ boolean moreOptionsVisible = moreOptionsVisible1;
+ boolean actionsPanelVisible = moreOptionsVisible1;
+
+ initMasterBreakpointPanel();
+
+ if (breakpoint.COUNT_FILTER > 0) {
+ myPassCountField.setText(Integer.toString(breakpoint.COUNT_FILTER));
+ moreOptionsVisible = true;
+ }
+ else {
+ myPassCountField.setText("");
+ }
+
+ PsiElement context = breakpoint.getEvaluationElement();
+ myPassCountCheckbox.setSelected(breakpoint.COUNT_FILTER_ENABLED);
+
+ myConditionCheckbox.setSelected(breakpoint.CONDITION_ENABLED);
+
+ myConditionCombo.setEnabled(breakpoint.CONDITION_ENABLED);
+
+ myConditionCombo.setContext(context);
+ myConditionCombo.setText(breakpoint.getCondition() != null ? breakpoint.getCondition() : emptyText());
+
+ myCbSuspend.setSelected(breakpoint.SUSPEND);
+ myRbSuspendThread.setEnabled(myCbSuspend.isSelected());
+ myRbSuspendAll.setEnabled(myCbSuspend.isSelected());
+
+ if(!breakpoint.SUSPEND) {
+ actionsPanelVisible = true;
+ }
+ if(DebuggerSettings.SUSPEND_THREAD.equals(breakpoint.SUSPEND_POLICY)){
+ myRbSuspendThread.setSelected(true);
+ }
+ else {
+ myRbSuspendAll.setSelected(true);
+ }
+
+ myCbSuspend.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ if (!myActionsPanel.isVisible()) {
+ if (!myCbSuspend.isSelected()) {
+ if (myDelegate != null) {
+ myDelegate.showActionsPanel();
+ }
+ }
+ }
+ myRbSuspendThread.setEnabled(myCbSuspend.isSelected());
+ myRbSuspendAll.setEnabled(myCbSuspend.isSelected());
+ }
+ });
+ myLogMessageCheckBox.setSelected(breakpoint.LOG_ENABLED);
+ myLogExpressionCheckBox.setSelected(breakpoint.LOG_EXPRESSION_ENABLED);
+ if (breakpoint.LOG_ENABLED || breakpoint.LOG_EXPRESSION_ENABLED) {
+ actionsPanelVisible = true;
+ }
+
+ myLogExpressionCombo.setContext(context);
+
+ if (breakpoint.getLogMessage() != null) {
+ myLogExpressionCombo.setText(breakpoint.getLogMessage());
+ }
+ else {
+ myLogExpressionCombo.setText(emptyText());
+ }
+
+ myLogExpressionCombo.setEnabled(breakpoint.LOG_EXPRESSION_ENABLED);
+ if (breakpoint.LOG_EXPRESSION_ENABLED) {
+ actionsPanelVisible = true;
+ }
+
+ myInstanceFiltersCheckBox.setSelected(breakpoint.INSTANCE_FILTERS_ENABLED);
+ myInstanceFiltersField.setEnabled(breakpoint.INSTANCE_FILTERS_ENABLED);
+ myInstanceFiltersField.getTextField().setEditable(breakpoint.INSTANCE_FILTERS_ENABLED);
+ myInstanceFilters = breakpoint.getInstanceFilters();
+ updateInstanceFilterEditor(true);
+ if (breakpoint.INSTANCE_FILTERS_ENABLED) {
+ moreOptionsVisible = true;
+ }
+
+ myClassFiltersCheckBox.setSelected(breakpoint.CLASS_FILTERS_ENABLED);
+ myClassFiltersField.setEnabled(breakpoint.CLASS_FILTERS_ENABLED);
+ myClassFiltersField.getTextField().setEditable(breakpoint.CLASS_FILTERS_ENABLED);
+ myClassFilters = breakpoint.getClassFilters();
+ myClassExclusionFilters = breakpoint.getClassExclusionFilters();
+ updateClassFilterEditor(true);
+ if (breakpoint.CLASS_FILTERS_ENABLED) {
+ moreOptionsVisible = true;
+ }
+
+ myBreakpointPsiClass = breakpoint.getPsiClass();
+
+ updateCheckboxes();
+
+ setActionsPanelVisible(actionsPanelVisible && !moreOptionsVisible1);
+ setMoreOptionsVisible(moreOptionsVisible);
+ }
+
+ private void initMasterBreakpointPanel() {
+ final EnableBreakpointRule rule = findMasterBreakpointRule();
+
+ final Breakpoint baseBreakpoint = rule != null ? rule.getMasterBreakpoint() : null;
+ updateMasterBreakpointPanel(rule);
+
+
+ myMasterBreakpointChooser = new BreakpointChooser(myProject, new BreakpointChooser.Delegate() {
+ @Override
+ public void breakpointChosen(Project project, BreakpointItem item) {
+ final boolean enabled = item != null && item.getBreakpoint() != null;
+ myLeaveEnabledRadioButton.setEnabled(enabled);
+ myDisableAgainRadio.setEnabled(enabled);
+ myEnableOrDisableLabel.setEnabled(enabled);
+
+ if (item != null) {
+
+ saveMasterBreakpoint();
+ }
+
+ updateMasterBreakpointPanel(findMasterBreakpointRule());
+
+ }
+ }, baseBreakpoint, getBreakpointItemsExceptMy());
+
+ insert(myDependentBreakpointComboPanel, myMasterBreakpointChooser.getComponent());
+
+ }
+
+ private @Nullable EnableBreakpointRule findMasterBreakpointRule() {
+ return myBreakpoint != null? getBreakpointManager(myProject).findBreakpointRule(myBreakpoint) : null;
+ }
+
+ private void updateMasterBreakpointPanel(@Nullable EnableBreakpointRule rule) {
+ final boolean leaveEnabled = rule != null && rule.isLeaveEnabled();
+ if (leaveEnabled) {
+ myLeaveEnabledRadioButton.setSelected(true);
+ }
+ else {
+ myDisableAgainRadio.setSelected(true);
+ }
+ }
+
+ private TextWithImportsImpl emptyText() {
+ return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "");
+ }
+
+ /**
+ * Save values in the UI components to the breakpoint object
+ */
+ public void saveTo(Breakpoint breakpoint, @NotNull Runnable afterUpdate) {
+
+ saveMasterBreakpoint();
+ try {
+ String text = myPassCountField.getText().trim();
+ int count = !"".equals(text)? Integer.parseInt(text) : 0;
+ breakpoint.COUNT_FILTER = count;
+ if (breakpoint.COUNT_FILTER < 0) {
+ breakpoint.COUNT_FILTER = 0;
+ }
+ }
+ catch (Exception e) {
+ }
+ breakpoint.COUNT_FILTER_ENABLED = breakpoint.COUNT_FILTER > 0 && myPassCountCheckbox.isSelected();
+ breakpoint.setCondition(myConditionCombo.getText());
+ breakpoint.CONDITION_ENABLED = myConditionCheckbox.isSelected();
+ breakpoint.setLogMessage(myLogExpressionCombo.getText());
+ breakpoint.LOG_EXPRESSION_ENABLED = !breakpoint.getLogMessage().isEmpty() && myLogExpressionCheckBox.isSelected();
+ breakpoint.LOG_ENABLED = myLogMessageCheckBox.isSelected();
+ breakpoint.SUSPEND = myCbSuspend.isSelected();
+ breakpoint.SUSPEND_POLICY = getSelectedSuspendPolicy();
+ reloadInstanceFilters();
+ reloadClassFilters();
+ updateInstanceFilterEditor(true);
+ updateClassFilterEditor(true);
+
+ breakpoint.INSTANCE_FILTERS_ENABLED = myInstanceFiltersField.getText().length() > 0 && myInstanceFiltersCheckBox.isSelected();
+ breakpoint.CLASS_FILTERS_ENABLED = myClassFiltersField.getText().length() > 0 && myClassFiltersCheckBox.isSelected();
+ breakpoint.setClassFilters(myClassFilters);
+ breakpoint.setClassExclusionFilters(myClassExclusionFilters);
+ breakpoint.setInstanceFilters(myInstanceFilters);
+
+ myConditionCombo.addRecent(myConditionCombo.getText());
+ myLogExpressionCombo.addRecent(myLogExpressionCombo.getText());
+ breakpoint.updateUI(afterUpdate);
+ }
+
+ private static String concatWithEx(List<String> s, String concator, int N, String NthConcator) {
+ String result = "";
+ int i = 1;
+ for (Iterator iterator = s.iterator(); iterator.hasNext(); i++) {
+ String str = (String) iterator.next();
+ result += str;
+ if(iterator.hasNext()){
+ if(i % N == 0){
+ result += NthConcator;
+ }
+ else {
+ result += concator;
+ }
+ }
+ }
+ return result;
+ }
+
+ private void updateInstanceFilterEditor(boolean updateText) {
+ List<String> filters = new ArrayList<String>();
+ for (InstanceFilter instanceFilter : myInstanceFilters) {
+ if (instanceFilter.isEnabled()) {
+ filters.add(Long.toString(instanceFilter.getId()));
+ }
+ }
+ if (updateText) {
+ myInstanceFiltersField.setText(StringUtil.join(filters, " "));
+ }
+
+ String tipText = concatWithEx(filters, " ", (int)Math.sqrt(myInstanceFilters.length) + 1, "\n");
+ myInstanceFiltersField.getTextField().setToolTipText(tipText);
+ }
+
+ private void reloadInstanceFilters() {
+ String filtersText = myInstanceFiltersField.getText();
+
+ ArrayList<InstanceFilter> idxs = new ArrayList<InstanceFilter>();
+ int startNumber = -1;
+ for(int i = 0; i <= filtersText.length(); i++) {
+ if(i < filtersText.length() && Character.isDigit(filtersText.charAt(i))) {
+ if(startNumber == -1) {
+ startNumber = i;
+ }
+ }
+ else {
+ if(startNumber >=0) {
+ idxs.add(InstanceFilter.create(filtersText.substring(startNumber, i)));
+ startNumber = -1;
+ }
+ }
+ }
+ for (InstanceFilter instanceFilter : myInstanceFilters) {
+ if (!instanceFilter.isEnabled()) {
+ idxs.add(instanceFilter);
+ }
+ }
+ myInstanceFilters = idxs.toArray(new InstanceFilter[idxs.size()]);
+ }
+
+ private void updateClassFilterEditor(boolean updateText) {
+ List<String> filters = new ArrayList<String>();
+ for (com.intellij.ui.classFilter.ClassFilter classFilter : myClassFilters) {
+ if (classFilter.isEnabled()) {
+ filters.add(classFilter.getPattern());
+ }
+ }
+ List<String> excludeFilters = new ArrayList<String>();
+ for (com.intellij.ui.classFilter.ClassFilter classFilter : myClassExclusionFilters) {
+ if (classFilter.isEnabled()) {
+ excludeFilters.add("-" + classFilter.getPattern());
+ }
+ }
+ if (updateText) {
+ String editorText = StringUtil.join(filters, " ");
+ if(!filters.isEmpty()) {
+ editorText += " ";
+ }
+ editorText += StringUtil.join(excludeFilters, " ");
+ myClassFiltersField.setText(editorText);
+ }
+
+ int width = (int)Math.sqrt(myClassExclusionFilters.length + myClassFilters.length) + 1;
+ String tipText = concatWithEx(filters, " ", width, "\n");
+ if(!filters.isEmpty()) {
+ tipText += "\n";
+ }
+ tipText += concatWithEx(excludeFilters, " ", width, "\n");
+ myClassFiltersField.getTextField().setToolTipText(tipText);
+ }
+
+ private void reloadClassFilters() {
+ String filtersText = myClassFiltersField.getText();
+
+ ArrayList<com.intellij.ui.classFilter.ClassFilter> classFilters = new ArrayList<com.intellij.ui.classFilter.ClassFilter>();
+ ArrayList<com.intellij.ui.classFilter.ClassFilter> exclusionFilters = new ArrayList<com.intellij.ui.classFilter.ClassFilter>();
+ int startFilter = -1;
+ for(int i = 0; i <= filtersText.length(); i++) {
+ if(i < filtersText.length() && !Character.isWhitespace(filtersText.charAt(i))){
+ if(startFilter == -1) {
+ startFilter = i;
+ }
+ }
+ else {
+ if(startFilter >=0) {
+ if(filtersText.charAt(startFilter) == '-') {
+ exclusionFilters.add(new com.intellij.ui.classFilter.ClassFilter(filtersText.substring(startFilter + 1, i)));
+ }
+ else {
+ classFilters.add(new com.intellij.ui.classFilter.ClassFilter(filtersText.substring(startFilter, i)));
+ }
+ startFilter = -1;
+ }
+ }
+ }
+ for (com.intellij.ui.classFilter.ClassFilter classFilter : myClassFilters) {
+ if (!classFilter.isEnabled()) {
+ classFilters.add(classFilter);
+ }
+ }
+ for (com.intellij.ui.classFilter.ClassFilter classFilter : myClassExclusionFilters) {
+ if (!classFilter.isEnabled()) {
+ exclusionFilters.add(classFilter);
+ }
+ }
+ myClassFilters = classFilters .toArray(new com.intellij.ui.classFilter.ClassFilter[classFilters .size()]);
+ myClassExclusionFilters = exclusionFilters.toArray(new com.intellij.ui.classFilter.ClassFilter[exclusionFilters.size()]);
+ }
+
+ public void setEnabled(boolean enabled) {
+ myPanel.setEnabled(enabled);
+ Component[] components = myPanel.getComponents();
+ for (Component component : components) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ protected void updateCheckboxes() {
+ JCheckBox [] checkBoxes = {myConditionCheckbox, myInstanceFiltersCheckBox, myClassFiltersCheckBox };
+ boolean passCountApplicable = true;
+ for (JCheckBox checkBox : checkBoxes) {
+ if (checkBox.isSelected()) {
+ passCountApplicable = false;
+ break;
+ }
+ }
+ myPassCountCheckbox.setEnabled(passCountApplicable);
+
+ final boolean passCountSelected = myPassCountCheckbox.isSelected();
+ for (JCheckBox checkBox : checkBoxes) {
+ checkBox.setEnabled(!passCountSelected);
+ }
+
+ myPassCountField.setEditable(myPassCountCheckbox.isSelected());
+ myPassCountField.setEnabled (myPassCountCheckbox.isSelected());
+
+ myConditionCombo.setEnabled(myConditionCheckbox.isSelected());
+ myConditionMagnifierButton.setEnabled(myConditionCheckbox.isSelected());
+
+ myInstanceFiltersField.setEnabled(myInstanceFiltersCheckBox.isSelected());
+ myInstanceFiltersField.getTextField().setEditable(myInstanceFiltersCheckBox.isSelected());
+
+ myClassFiltersField.setEnabled(myClassFiltersCheckBox.isSelected());
+ myClassFiltersField.getTextField().setEditable(myClassFiltersCheckBox.isSelected());
+ }
+
+ public JPanel getPanel() {
+ return myPanel;
+ }
+
+ private static BreakpointManager getBreakpointManager(Project project) {
+ return DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ }
+
+ private static class MagnifierButtonAction implements ActionListener {
+ private final Project myProject;
+ private final CompletionEditor myTargetEditor;
+ private final String myDialogTitle;
+ private DebuggerStatementEditor myEditor;
+
+ private MagnifierButtonAction(final Project project, final CompletionEditor targetEditor, final String dialogTitle) {
+ myProject = project;
+ myTargetEditor = targetEditor;
+ myDialogTitle = dialogTitle;
+ }
+
+ public void actionPerformed(final ActionEvent e) {
+ new DialogWrapper(myTargetEditor, true){
+ public void show() {
+ setTitle(myDialogTitle);
+ setModal(true);
+ init();
+ super.show();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myEditor;
+ }
+
+ @Nullable
+ protected JComponent createCenterPanel() {
+ final JPanel panel = new JPanel(new BorderLayout());
+ myEditor = new DebuggerStatementEditor(myProject, myTargetEditor.getContext(), myTargetEditor.getRecentsId(), DefaultCodeFragmentFactory.getInstance());
+ myEditor.setPreferredSize(new Dimension(400, 150));
+ myEditor.setText(myTargetEditor.getText());
+ panel.add(myEditor, BorderLayout.CENTER);
+ return panel;
+ }
+
+ protected void doOKAction() {
+ myTargetEditor.setText(myEditor.getText());
+ super.doOKAction();
+ }
+ }.show();
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTable.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTable.java
new file mode 100644
index 0000000..7530fa5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTable.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.util.ui.Table;
+import com.intellij.xdebugger.XDebuggerBundle;
+
+import javax.swing.*;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 23, 2005
+ */
+public class BreakpointTable extends Table {
+ public BreakpointTable(final Project project) {
+ super(new BreakpointTableModel(project));
+ setColumnSelectionAllowed(false);
+ InputMap inputMap = getInputMap();
+ ActionMap actionMap = getActionMap();
+ Object o = inputMap.get(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0));
+ if (o == null) {
+ //noinspection HardCodedStringLiteral
+ o = "enable_disable";
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), o);
+ }
+ actionMap.put(o, new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ if (isEditing()) {
+ return;
+ }
+ int[] indices = getSelectedRows();
+ boolean currentlyMarked = true;
+ for (int i = 0; i < indices.length; i++) {
+ final Boolean isMarked = (Boolean)getValueAt(indices[i], BreakpointTableModel.ENABLED_STATE);
+ currentlyMarked = isMarked != null? isMarked.booleanValue() : false;
+ if (!currentlyMarked) {
+ break;
+ }
+ }
+ final Boolean valueToSet = currentlyMarked ? Boolean.FALSE : Boolean.TRUE;
+ for (int i = 0; i < indices.length; i++) {
+ setValueAt(valueToSet, indices[i], BreakpointTableModel.ENABLED_STATE);
+ }
+ }
+ });
+
+ setShowGrid(false);
+ setIntercellSpacing(new Dimension(0, 0));
+ setTableHeader(null);
+ setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+ setColumnSelectionAllowed(false);
+
+ int width = new JCheckBox().getPreferredSize().width;
+ TableColumnModel columnModel = getColumnModel();
+
+ TableColumn enabledStateColumn = columnModel.getColumn(BreakpointTableModel.ENABLED_STATE);
+ enabledStateColumn.setPreferredWidth(width);
+ enabledStateColumn.setMaxWidth(width);
+ final Class enabledStateColumnClass = getModel().getColumnClass(BreakpointTableModel.ENABLED_STATE);
+ final TableCellRenderer delegateRenderer = getDefaultRenderer(enabledStateColumnClass);
+ setDefaultRenderer(enabledStateColumnClass, new DefaultTableCellRenderer() {
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ final Component component = delegateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ if (component instanceof JComponent) {
+ ((JComponent)component).setBorder(null);
+ }
+ return component;
+ }
+ });
+ columnModel.getColumn(BreakpointTableModel.NAME).setCellRenderer(new BreakpointNameCellRenderer());
+
+ getEmptyText().setText(XDebuggerBundle.message("debugger.no.breakpoints"));
+ }
+
+ public BreakpointTableModel getModel() {
+ return (BreakpointTableModel)super.getModel();
+ }
+
+ public void setBreakpoints(Breakpoint[] breakpoints) {
+ getModel().setBreakpoints(breakpoints);
+ }
+
+ public final java.util.List<Breakpoint> getBreakpoints() {
+ return getModel().getBreakpoints();
+ }
+
+ public Breakpoint[] getSelectedBreakpoints() {
+ if (getRowCount() == 0) {
+ return Breakpoint.EMPTY_ARRAY;
+ }
+
+ int[] rows = getSelectedRows();
+ if (rows.length == 0) {
+ return Breakpoint.EMPTY_ARRAY;
+ }
+ Breakpoint[] rv = new Breakpoint[rows.length];
+ for (int idx = 0; idx < rows.length; idx++) {
+ rv[idx] = getModel().getBreakpoint(rows[idx]);
+ }
+ return rv;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTableModel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTableModel.java
new file mode 100644
index 0000000..6999260
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTableModel.java
@@ -0,0 +1,161 @@
+/*
+ * 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 BreakpointTableModel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.ItemRemovable;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class BreakpointTableModel extends AbstractTableModel implements ItemRemovable {
+ public static final int ENABLED_STATE = 0;
+ public static final int NAME = 1;
+
+ private java.util.List<Breakpoint> myBreakpoints = null;
+ private final BreakpointManager myBreakpointManager;
+
+ public BreakpointTableModel(final Project project) {
+ myBreakpoints = new ArrayList<Breakpoint>();
+ myBreakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ }
+
+ public final void setBreakpoints(Breakpoint[] breakpoints) {
+ myBreakpoints.clear();
+ if (breakpoints != null) {
+ ContainerUtil.addAll(myBreakpoints, breakpoints);
+ }
+ fireTableDataChanged();
+ }
+
+ public List<Breakpoint> getBreakpoints() {
+ return Collections.unmodifiableList(myBreakpoints);
+ }
+
+ public void removeBreakpoints(Breakpoint[] breakpoints) {
+ myBreakpoints.removeAll(Arrays.asList(breakpoints));
+ fireTableDataChanged();
+ }
+
+ public Breakpoint getBreakpoint(int index) {
+ if (index < 0 || index >= myBreakpoints.size()) return null;
+ return myBreakpoints.get(index);
+ }
+
+ public boolean isBreakpointEnabled(int index) {
+ return ((Boolean)getValueAt(index, ENABLED_STATE)).booleanValue();
+ }
+
+ public int getBreakpointIndex(Breakpoint breakpoint) {
+ return myBreakpoints.indexOf(breakpoint);
+ }
+
+ public void insertBreakpointAt(Breakpoint breakpoint, int index) {
+ myBreakpoints.add(index, breakpoint);
+ fireTableRowsInserted(index, index);
+ }
+
+ public void addBreakpoint(Breakpoint breakpoint) {
+ myBreakpoints.add(breakpoint);
+ int row = myBreakpoints.size() - 1;
+ fireTableRowsInserted(row, row);
+ }
+
+ public void removeRow(int idx) {
+ if (idx >= 0 && idx < myBreakpoints.size()) {
+ myBreakpoints.remove(idx);
+ fireTableRowsDeleted(idx, idx);
+ }
+ }
+
+ public int getRowCount() {
+ return myBreakpoints.size();
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case ENABLED_STATE:
+ return DebuggerBundle.message("breakpoint.table.header.column.enabled");
+ case NAME:
+ return DebuggerBundle.message("breakpoint.table.header.column.name");
+ default :
+ return "";
+ }
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ Breakpoint breakpoint = myBreakpoints.get(rowIndex);
+ if (columnIndex == NAME) {
+ return breakpoint.getDisplayName();
+ }
+ if (columnIndex == ENABLED_STATE) {
+ return breakpoint.ENABLED? Boolean.TRUE : Boolean.FALSE;
+ }
+ return null;
+ }
+
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ if (rowIndex < 0 || rowIndex >= myBreakpoints.size()) {
+ return;
+ }
+ Breakpoint breakpoint = myBreakpoints.get(rowIndex);
+/*
+ if (columnIndex == NAME) {
+ breakpoint.setDisplayName((aValue != null)? aValue.toString() : "");
+ }
+ else
+*/
+ if (columnIndex == ENABLED_STATE) {
+ final boolean isEnabled = aValue == null || ((Boolean)aValue).booleanValue();
+ final boolean valueChanged = isEnabled != breakpoint.ENABLED;
+ breakpoint.ENABLED = isEnabled;
+ if (valueChanged) {
+ breakpoint.updateUI();
+ }
+ }
+ fireTableRowsUpdated(rowIndex, rowIndex);
+ }
+
+ public Class getColumnClass(int columnIndex) {
+ if (columnIndex == ENABLED_STATE) {
+ return Boolean.class;
+ }
+ return super.getColumnClass(columnIndex);
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ if (columnIndex != ENABLED_STATE) {
+ return false;
+ }
+ final boolean isSlave = myBreakpointManager.findMasterBreakpoint(myBreakpoints.get(rowIndex)) != null;
+ return !isSlave;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTree.java
new file mode 100644
index 0000000..2d39fe0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointTree.java
@@ -0,0 +1,821 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.ui.tree.TreeUtil;
+import com.intellij.xdebugger.XDebuggerBundle;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 20, 2005
+ */
+public class BreakpointTree extends CheckboxTree {
+ private static final String DEFAULT_PACKAGE_NAME = DebuggerBundle.message("default.package.name");
+ private final CheckedTreeNode myRootNode;
+ private final List<Breakpoint> myBreakpoints = new ArrayList<Breakpoint>();
+ private final Map<TreeDescriptor, CheckedTreeNode> myDescriptorToNodeMap = new HashMap<TreeDescriptor, CheckedTreeNode>();
+
+ private boolean myGroupByMethods = false;
+ private boolean myGroupByClasses = true;
+ private boolean myFlattenPackages = true;
+
+ private final NodeAppender[] myAppenders = {
+ new BreakpointToMethodAppender(),
+ new BreakpointToClassAppender(),
+ new BreakpointToPackageAppender(),
+ new MethodToClassAppender(),
+ new MethodToPackageAppender(),
+ new ClassToPackageAppender(),
+ new PackageToPackageAppender(),
+ };
+
+ private final Comparator<CheckedTreeNode> myNodeComparator = new Comparator<CheckedTreeNode>() {
+ public int compare(CheckedTreeNode o1, CheckedTreeNode o2) {
+ final int w1 = getWeight(o1);
+ final int w2 = getWeight(o2);
+ if (w1 != w2) {
+ return w1 - w2;
+ }
+ final TreeDescriptor d1 = (TreeDescriptor)o1.getUserObject();
+ final TreeDescriptor d2 = (TreeDescriptor)o2.getUserObject();
+ if (d1 instanceof BreakpointDescriptor && d2 instanceof BreakpointDescriptor) {
+ return 0;
+ }
+ return d1.getDisplayString().compareTo(d2.getDisplayString());
+ }
+
+ private int getWeight(CheckedTreeNode node) {
+ if (node.getUserObject() instanceof BreakpointDescriptor) {
+ return 100;
+ }
+ if (node.getUserObject() instanceof MethodDescriptor) {
+ return 90;
+ }
+ if (node.getUserObject() instanceof PackageDescriptor) {
+ return 80;
+ }
+ if (node.getUserObject() instanceof ClassDescriptor) {
+ return 70;
+ }
+ return 50;
+ }
+ };
+ private final BreakpointManager myBreakpointManager;
+ private final BreakpointManagerListener myNodeUpdateListener;
+
+ protected void installSpeedSearch() {
+ new TreeSpeedSearch(this, new Convertor<TreePath, String>() {
+ public String convert(TreePath path) {
+ final CheckedTreeNode node = (CheckedTreeNode)path.getLastPathComponent();
+ return ((TreeDescriptor)node.getUserObject()).getDisplayString();
+ }
+ });
+ }
+
+ public boolean getExpandsSelectedPaths() {
+ return true;
+ }
+
+ public Breakpoint[] getSelectedBreakpoints() {
+ final TreePath[] selectionPaths = getSelectionPaths();
+ if (selectionPaths == null || selectionPaths.length == 0) {
+ return Breakpoint.EMPTY_ARRAY;
+ }
+ final List<Breakpoint> breakpoints = new ArrayList<Breakpoint>(selectionPaths.length);
+ for (TreePath path : selectionPaths) {
+ final CheckedTreeNode node = (CheckedTreeNode)path.getLastPathComponent();
+ TreeUtil.traverseDepth(node, new TreeUtil.Traverse() {
+ public boolean accept(Object _node) {
+ final CheckedTreeNode node = (CheckedTreeNode)_node;
+ final TreeDescriptor descriptor = (TreeDescriptor)node.getUserObject();
+ if (descriptor instanceof BreakpointDescriptor) {
+ breakpoints.add(((BreakpointDescriptor)descriptor).getBreakpoint());
+ }
+ return true;
+ }
+ });
+ }
+ return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
+ }
+
+ public void selectBreakpoint(Breakpoint breakpoint) {
+ final CheckedTreeNode node = myDescriptorToNodeMap.get(new BreakpointDescriptor(breakpoint));
+ if (node == null) {
+ return;
+ }
+ TreeUtil.selectNode(this, node);
+ }
+
+ public void selectBreakpoints(Breakpoint[] breakpoints) {
+ final List<CheckedTreeNode> nodes = new ArrayList<CheckedTreeNode>(breakpoints.length);
+ for (Breakpoint breakpoint : breakpoints) {
+ final CheckedTreeNode node = myDescriptorToNodeMap.get(new BreakpointDescriptor(breakpoint));
+ if (node != null) {
+ nodes.add(node);
+ }
+ }
+ clearSelection();
+ for (CheckedTreeNode node : nodes) {
+ addSelectionPath(new TreePath(node.getPath()));
+ }
+ }
+
+ public void selectFirstBreakpoint() {
+ TreeUtil.traverseDepth(myRootNode, new TreeUtil.Traverse() {
+ public boolean accept(Object node) {
+ final CheckedTreeNode treeNode = (CheckedTreeNode)node;
+ final TreeDescriptor descriptor = (TreeDescriptor)treeNode.getUserObject();
+ if (descriptor instanceof BreakpointDescriptor) {
+ TreeUtil.selectNode(BreakpointTree.this, treeNode);
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+
+ public List<Breakpoint> getBreakpoints() {
+ return Collections.unmodifiableList(myBreakpoints);
+ }
+
+ public void dispose() {
+ final KeyStroke[] treeStrokes = getRegisteredKeyStrokes();
+ for (KeyStroke stroke : treeStrokes) {
+ unregisterKeyboardAction(stroke);
+ }
+ myBreakpointManager.removeBreakpointManagerListener(myNodeUpdateListener);
+ }
+
+ private abstract static class TreeDescriptor {
+ protected void customizeCellRenderer(final ColoredTreeCellRenderer targetRenderer, CheckedTreeNode node, boolean selected, final boolean checked, boolean expanded, boolean leaf, boolean hasFocus) {
+ targetRenderer.setIcon(getDisplayIcon());
+ targetRenderer.append(getDisplayString(), checked? SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+
+ protected abstract String getDisplayString();
+
+ protected abstract Icon getDisplayIcon();
+
+ }
+
+ private final class BreakpointDescriptor extends TreeDescriptor {
+ private final Breakpoint myBreakpoint;
+ public BreakpointDescriptor(Breakpoint breakpoint) {
+ myBreakpoint = breakpoint;
+ }
+ @NotNull
+ public Breakpoint getBreakpoint() {
+ return myBreakpoint;
+ }
+
+ protected Icon getDisplayIcon() {
+ return myBreakpoint instanceof BreakpointWithHighlighter ?
+ myBreakpoint.ENABLED? ((BreakpointWithHighlighter)myBreakpoint).getSetIcon(false) : ((BreakpointWithHighlighter)myBreakpoint).getDisabledIcon(false) : myBreakpoint.getIcon();
+ }
+
+ public String getDisplayString() {
+ return myBreakpoint.getDisplayName();
+ }
+
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final BreakpointDescriptor breakpointDescriptor = (BreakpointDescriptor)o;
+ return myBreakpoint.equals(breakpointDescriptor.myBreakpoint);
+ }
+
+ public int hashCode() {
+ return myBreakpoint.hashCode();
+ }
+
+ public boolean isSlave() {
+ final Breakpoint breakpoint = getBreakpoint();
+ return myBreakpointManager.findMasterBreakpoint(breakpoint) != null;
+ }
+ }
+
+ private static final class MethodDescriptor extends TreeDescriptor {
+ private final String myClassName;
+ private final String myMethodName;
+ @NotNull private final String myPackageName;
+
+ public MethodDescriptor(String methodName, String className, @NotNull String packageName) {
+ myClassName = className;
+ myMethodName = methodName;
+ myPackageName = packageName;
+ }
+
+ @NotNull
+ public String getPackageName() {
+ return myPackageName;
+ }
+
+ public String getClassName() {
+ return myClassName;
+ }
+
+ public String getMethodName() {
+ return myMethodName;
+ }
+
+ protected String getDisplayString() {
+ return myMethodName;
+ }
+
+ protected Icon getDisplayIcon() {
+ return PlatformIcons.METHOD_ICON;
+ }
+
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final MethodDescriptor methodDescriptor = (MethodDescriptor)o;
+ if (!myClassName.equals(methodDescriptor.myClassName)) {
+ return false;
+ }
+ return myMethodName.equals(methodDescriptor.myMethodName);
+ }
+
+ public int hashCode() {
+ int result = myClassName.hashCode();
+ result = 29 * result + myMethodName.hashCode();
+ return result;
+ }
+ }
+
+ private static final class ClassDescriptor extends TreeDescriptor {
+ @NotNull private final String myClassName;
+ @NotNull private final String myPackageName;
+
+ public ClassDescriptor(@NotNull String className, @NotNull String packageName) {
+ myClassName = className;
+ myPackageName = packageName.length() == 0? DEFAULT_PACKAGE_NAME : packageName;
+ }
+
+ @NotNull
+ public String getPackageName() {
+ return myPackageName;
+ }
+
+ @NotNull
+ public String getClassName() {
+ return myClassName;
+ }
+
+ protected String getDisplayString() {
+ return getClassName();
+ }
+
+ protected Icon getDisplayIcon() {
+ return PlatformIcons.CLASS_ICON;
+ }
+
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final ClassDescriptor classDescriptor = (ClassDescriptor)o;
+
+ return myClassName.equals(classDescriptor.myClassName);
+
+ }
+
+ public int hashCode() {
+ return myClassName.hashCode();
+ }
+ }
+
+ private static final class PackageDescriptor extends TreeDescriptor {
+ private final String myPackageName;
+
+ public PackageDescriptor(String packageName) {
+ myPackageName = "".equals(packageName)? DEFAULT_PACKAGE_NAME : packageName;
+ }
+
+ public String getPackageName() {
+ return myPackageName;
+ }
+
+ public String getParentPackageName() {
+ final int dotIndex = myPackageName.lastIndexOf('.');
+ return dotIndex >= 0 ? myPackageName.substring(0, dotIndex) : null;
+ }
+
+ public void customizeCellRenderer(final ColoredTreeCellRenderer targetRenderer, CheckedTreeNode node, boolean selected,
+ final boolean checked, boolean expanded, boolean leaf, boolean hasFocus) {
+ targetRenderer.setIcon(PlatformIcons.PACKAGE_ICON);
+ final String displayName;
+ final CheckedTreeNode parent = (CheckedTreeNode)node.getParent();
+ if (parent != null && parent.getUserObject() instanceof PackageDescriptor) {
+ final String parentPackageInTree = ((PackageDescriptor)parent.getUserObject()).getPackageName() + ".";
+ displayName = myPackageName.startsWith(parentPackageInTree)? myPackageName.substring(parentPackageInTree.length()) : myPackageName;
+ }
+ else {
+ displayName = myPackageName;
+ }
+ targetRenderer.append(displayName, checked? SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+
+ protected String getDisplayString() {
+ return myPackageName;
+ }
+
+ protected Icon getDisplayIcon() {
+ return PlatformIcons.PACKAGE_ICON;
+ }
+
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final PackageDescriptor packageDescriptor = (PackageDescriptor)o;
+
+ return myPackageName.equals(packageDescriptor.myPackageName);
+
+ }
+
+ public int hashCode() {
+ return myPackageName.hashCode();
+ }
+ }
+
+ public BreakpointTree(final Project project) {
+ super(new BreakpointTreeCellRenderer(), new CheckedTreeNode(new RootDescriptor()));
+ myRootNode = (CheckedTreeNode)getModel().getRoot();
+ myDescriptorToNodeMap.put((TreeDescriptor)myRootNode.getUserObject(), myRootNode);
+ myBreakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ myNodeUpdateListener = new BreakpointManagerListener() {
+ public void breakpointsChanged() {
+ repaint();
+ }
+ };
+ myBreakpointManager.addBreakpointManagerListener(myNodeUpdateListener);
+ getEmptyText().setText(XDebuggerBundle.message("debugger.no.breakpoints"));
+ }
+
+ public boolean isGroupByMethods() {
+ return myGroupByMethods;
+ }
+
+ public void setGroupByMethods(boolean groupByMethods) {
+ if (myGroupByMethods != groupByMethods) {
+ myGroupByMethods = groupByMethods;
+ rebuildTree();
+ }
+ }
+
+ public boolean isGroupByClasses() {
+ return myGroupByClasses;
+ }
+
+ public void setGroupByClasses(boolean groupByClasses) {
+ if (myGroupByClasses != groupByClasses) {
+ myGroupByClasses = groupByClasses;
+ rebuildTree();
+ }
+ }
+
+ public boolean isFlattenPackages() {
+ return myFlattenPackages;
+ }
+
+ public void setFlattenPackages(boolean flattenPackages) {
+ if (myFlattenPackages != flattenPackages) {
+ myFlattenPackages = flattenPackages;
+ rebuildTree();
+ }
+ }
+
+ @Override
+ protected void onNodeStateChanged(final CheckedTreeNode node) {
+ final Object descriptor = node.getUserObject();
+ final Breakpoint breakpoint;
+ if (descriptor instanceof BreakpointDescriptor) {
+ breakpoint = ((BreakpointDescriptor)descriptor).getBreakpoint();
+ if (myBreakpointManager.findMasterBreakpoint(breakpoint) != null) {
+ return;
+ }
+ }
+ else {
+ breakpoint = null;
+ }
+
+ if (breakpoint != null) {
+ myBreakpointManager.setBreakpointEnabled(breakpoint, node.isChecked());
+ }
+
+ }
+
+ public void addBreakpoint(final Breakpoint breakpoint) {
+ myBreakpoints.add(breakpoint);
+ breakpoint.updateUI(new Runnable() {
+ public void run() {
+ rebuildTree();
+ }
+ });
+ rebuildTree();
+ selectBreakpoint(breakpoint);
+ }
+
+ public void removeBreakpoint(Breakpoint breakpoint) {
+ myBreakpoints.remove(breakpoint);
+ rebuildTree();
+ }
+
+ public void removeBreakpoints(Breakpoint[] breakpoints) {
+ myBreakpoints.removeAll(Arrays.asList(breakpoints));
+ rebuildTree();
+ }
+
+ public void setBreakpoints(Breakpoint[] breakpoints) {
+ myBreakpoints.clear();
+ ContainerUtil.addAll(myBreakpoints, breakpoints);
+ rebuildTree();
+ }
+
+ public Breakpoint getPreviousSibling(Breakpoint breakpoint) {
+ return getSibling(breakpoint, false);
+ }
+
+
+ public Breakpoint getNextSibling(Breakpoint breakpoint) {
+ return getSibling(breakpoint, true);
+ }
+
+ private Breakpoint getSibling(Breakpoint breakpoint, boolean nextSibling) {
+ final CheckedTreeNode node = myDescriptorToNodeMap.get(new BreakpointDescriptor(breakpoint));
+ if (node == null) {
+ return null;
+ }
+ final CheckedTreeNode sibling = (CheckedTreeNode) (nextSibling? node.getNextSibling() : node.getPreviousSibling());
+ if (sibling == null) {
+ return null;
+ }
+ final TreeDescriptor descriptor = (TreeDescriptor)sibling.getUserObject();
+ return descriptor instanceof BreakpointDescriptor ? ((BreakpointDescriptor)descriptor).getBreakpoint() : null;
+ }
+
+ private void rebuildTree() {
+ final TreeStateSnapshot treeStateSnapshot = new TreeStateSnapshot(this);
+ myRootNode.removeAllChildren();
+ myDescriptorToNodeMap.clear();
+ myDescriptorToNodeMap.put((TreeDescriptor)myRootNode.getUserObject(), myRootNode);
+ // build tree
+ for (final Breakpoint breakpoint : myBreakpoints) {
+ CheckedTreeNode node = createNode(new BreakpointDescriptor(breakpoint));
+ node.setChecked(breakpoint.ENABLED);
+ addNode(node);
+ }
+ // remove all package nodes with one child
+ final int count = myRootNode.getChildCount();
+ final List<CheckedTreeNode> children = new ArrayList<CheckedTreeNode>();
+ for (int idx = 0; idx < count; idx++) {
+ CheckedTreeNode child = (CheckedTreeNode)myRootNode.getChildAt(idx);
+ if (!(child.getUserObject() instanceof PackageDescriptor)) {
+ children.add(child);
+ continue;
+ }
+ while (child.getUserObject() instanceof PackageDescriptor && child.getChildCount() <= 1) {
+ child = (CheckedTreeNode)child.getChildAt(0);
+ }
+ if (!(child.getUserObject() instanceof PackageDescriptor)) {
+ child = (CheckedTreeNode)child.getParent();
+ }
+ for (CheckedTreeNode childToRemove = (CheckedTreeNode)child.getParent(); !childToRemove.equals(myRootNode); childToRemove = (CheckedTreeNode)childToRemove.getParent()) {
+ myDescriptorToNodeMap.remove(childToRemove.getUserObject());
+ }
+ children.add(child);
+ }
+ for (final CheckedTreeNode aChildren : children) {
+ aChildren.removeFromParent();
+ }
+ myRootNode.removeAllChildren();
+ for (final CheckedTreeNode child : children) {
+ myRootNode.add(child);
+ }
+ sortChildren(myRootNode);
+ ((DefaultTreeModel)getModel()).nodeStructureChanged(myRootNode);
+ treeStateSnapshot.restore(this);
+ expandPath(new TreePath(myRootNode));
+ }
+
+ private void sortChildren(CheckedTreeNode node) {
+ final int childCount = node.getChildCount();
+ if (childCount == 0) {
+ return;
+ }
+ final List<CheckedTreeNode> children = new ArrayList<CheckedTreeNode>(childCount);
+ for (int idx = 0; idx < childCount; idx++) {
+ children.add((CheckedTreeNode)node.getChildAt(idx));
+ }
+ for (CheckedTreeNode child : children) {
+ sortChildren(child);
+ child.removeFromParent();
+ }
+ Collections.sort(children, myNodeComparator);
+ for (CheckedTreeNode child : children) {
+ node.add(child);
+ }
+ }
+
+ @NotNull
+ private CheckedTreeNode createNode(final TreeDescriptor descriptor) {
+ final CheckedTreeNode node = new CheckedTreeNode(descriptor);
+ myDescriptorToNodeMap.put(descriptor, node);
+ return node;
+ }
+
+ /**
+ * @param node
+ */
+ private void addNode(CheckedTreeNode node) {
+ for (final NodeAppender appender : myAppenders) {
+ node = appender.append(node);
+ if (node == null) {
+ break;
+ }
+ }
+ if (node != null) {
+ attachNodeToParent(getDescriptor(myRootNode), node);
+ }
+ }
+
+ private static TreeDescriptor getDescriptor(final CheckedTreeNode node) {
+ return (TreeDescriptor)node.getUserObject();
+ }
+
+ /**
+ * @param parentDescriptor a descriptor of the childNode (possibly not existing) to attach to
+ * @param childNode the childNode to be attached
+ * @return either parent node if it has just been created or null, if the node has been attached to already existing childNode
+ */
+ private CheckedTreeNode attachNodeToParent(final TreeDescriptor parentDescriptor, final CheckedTreeNode childNode) {
+ CheckedTreeNode parentNode = myDescriptorToNodeMap.get(parentDescriptor);
+ try {
+ if (parentNode != null) {
+ parentNode.add(childNode);
+ return null; // added to already existing, so stop iteration over appenders
+ }
+ parentNode = createNode(parentDescriptor);
+ parentNode.add(childNode);
+ return parentNode;
+ }
+ finally {
+ if (parentNode != null && parentNode.getChildCount() == 1) {
+ expandPath(new TreePath(parentNode.getPath()));
+ }
+ }
+ }
+
+ private static class BreakpointTreeCellRenderer extends CheckboxTreeCellRenderer {
+ public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ if (value instanceof CheckedTreeNode) {
+ final CheckedTreeNode node = (CheckedTreeNode)value;
+ final TreeDescriptor descriptor = getDescriptor(node);
+ descriptor.customizeCellRenderer(getTextRenderer(), node, selected, node.isChecked(), expanded, leaf, hasFocus);
+ if (descriptor instanceof BreakpointDescriptor) {
+ myCheckbox.setEnabled(node.isEnabled() && !((BreakpointDescriptor)descriptor).isSlave());
+ }
+ }
+ }
+ }
+
+ private abstract static class NodeAppender {
+ public abstract CheckedTreeNode append(CheckedTreeNode node);
+ }
+
+ private class BreakpointToMethodAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ if (!myGroupByMethods) {
+ return node;
+ }
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof BreakpointDescriptor)) {
+ return node;
+ }
+ final Breakpoint breakpoint = ((BreakpointDescriptor)descriptor).getBreakpoint();
+ if (!(breakpoint instanceof LineBreakpoint)) {
+ return node;
+ }
+ final LineBreakpoint lineBreakpoint = (LineBreakpoint)breakpoint;
+ final String methodName = lineBreakpoint.getMethodName();
+ final String className = lineBreakpoint.getShortClassName();
+ final String packageName = lineBreakpoint.getPackageName();
+ if (methodName == null || className == null || packageName == null) {
+ return node;
+ }
+ return attachNodeToParent(new MethodDescriptor(methodName, className, packageName), node);
+ }
+ }
+
+ private class BreakpointToClassAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ if (!myGroupByClasses) {
+ return node;
+ }
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof BreakpointDescriptor)) {
+ return node;
+ }
+
+ final Breakpoint breakpoint = ((BreakpointDescriptor)descriptor).getBreakpoint();
+ final String className = breakpoint.getShortClassName();
+ if (className == null) {
+ return node;
+ }
+ final String packageName = breakpoint.getPackageName();
+ if (packageName == null) {
+ return node;
+ }
+ return attachNodeToParent(new ClassDescriptor(className, packageName), node);
+ }
+ }
+
+ private class BreakpointToPackageAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof BreakpointDescriptor)) {
+ return node;
+ }
+
+ final Breakpoint breakpoint = ((BreakpointDescriptor)descriptor).getBreakpoint();
+ final String packageName;
+ if (breakpoint instanceof ExceptionBreakpoint) {
+ packageName = breakpoint.getPackageName();
+ }
+ else if (breakpoint instanceof BreakpointWithHighlighter) {
+ packageName = breakpoint.getPackageName();
+ }
+ else {
+ packageName = null;
+ }
+ if (packageName == null) {
+ return node;
+ }
+ return attachNodeToParent(new PackageDescriptor(packageName), node);
+ }
+ }
+
+ private class MethodToClassAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ if (!myGroupByClasses) {
+ return node;
+ }
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof MethodDescriptor)) {
+ return node;
+ }
+ final MethodDescriptor methodDescriptor = (MethodDescriptor)descriptor;
+ final String className = methodDescriptor.getClassName();
+ final String packageName = methodDescriptor.getPackageName();
+ return attachNodeToParent(new ClassDescriptor(className, packageName), node);
+ }
+ }
+
+ private class MethodToPackageAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof MethodDescriptor)) {
+ return node;
+ }
+ final MethodDescriptor methodDescriptor = (MethodDescriptor)descriptor;
+ final String packageName = methodDescriptor.getPackageName();
+ return attachNodeToParent(new PackageDescriptor(packageName), node);
+ }
+ }
+
+ private class ClassToPackageAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof ClassDescriptor)) {
+ return node;
+ }
+
+ final String packageName = ((ClassDescriptor)descriptor).getPackageName();
+ return attachNodeToParent(new PackageDescriptor(packageName), node);
+ }
+ }
+
+ private class PackageToPackageAppender extends NodeAppender {
+ public CheckedTreeNode append(CheckedTreeNode node) {
+ if (myFlattenPackages) {
+ return node;
+ }
+ final TreeDescriptor descriptor = getDescriptor(node);
+ if (!(descriptor instanceof PackageDescriptor)) {
+ return node;
+ }
+
+ final PackageDescriptor packageDescriptor = (PackageDescriptor)descriptor;
+ final String parentPackageName = packageDescriptor.getParentPackageName();
+ if (parentPackageName == null) {
+ return node;
+ }
+ final CheckedTreeNode parentNode = attachNodeToParent(new PackageDescriptor(parentPackageName), node);
+ if (parentNode == null) {
+ return null;
+ }
+ return append(parentNode);
+ }
+ }
+
+ private static class RootDescriptor extends TreeDescriptor {
+
+ protected String getDisplayString() {
+ return "";
+ }
+
+ protected Icon getDisplayIcon() {
+ return PlatformIcons.PROJECT_ICON;
+ }
+ }
+
+ private static class TreeStateSnapshot {
+ private final Object[] myExpandedUserObjects;
+ private final Object[] mySelectedUserObjects;
+
+ public TreeStateSnapshot(BreakpointTree tree) {
+ final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree);
+ myExpandedUserObjects = getUserObjects(expandedPaths.toArray(new TreePath[expandedPaths.size()]));
+ mySelectedUserObjects =getUserObjects(tree.getSelectionPaths());
+ }
+
+ private static Object[] getUserObjects(final TreePath[] treePaths) {
+ if (treePaths == null) {
+ return ArrayUtil.EMPTY_OBJECT_ARRAY;
+ }
+ Object[] userObjects = new Object[treePaths.length];
+ int index = 0;
+ for (TreePath path : treePaths) {
+ userObjects[index++] = ((CheckedTreeNode)path.getLastPathComponent()).getUserObject();
+ }
+ return userObjects;
+ }
+
+ public void restore(BreakpointTree tree) {
+ final List<TreePath> pathsToExpand = getPaths(tree, myExpandedUserObjects);
+ if (!pathsToExpand.isEmpty()) {
+ TreeUtil.restoreExpandedPaths(tree, pathsToExpand);
+ }
+
+ final List<TreePath> pathsToSelect = getPaths(tree, mySelectedUserObjects);
+ if (!pathsToSelect.isEmpty()) {
+ tree.getSelectionModel().clearSelection();
+ tree.setSelectionPaths(pathsToSelect.toArray(new TreePath[pathsToSelect.size()]));
+ }
+ }
+
+ private static List<TreePath> getPaths(BreakpointTree tree, final Object[] userObjects) {
+ final List<TreePath> paths = new ArrayList<TreePath>(userObjects.length);
+ for (Object descriptor : userObjects) {
+ final CheckedTreeNode node = tree.myDescriptorToNodeMap.get(descriptor);
+ if (node != null) {
+ paths.add(new TreePath(node.getPath()));
+ }
+ }
+ return paths;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java
new file mode 100644
index 0000000..a285463
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java
@@ -0,0 +1,737 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.CommonBundle;
+import com.intellij.debugger.*;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.ex.MarkupModelEx;
+import com.intellij.openapi.editor.impl.DocumentMarkupModel;
+import com.intellij.openapi.editor.markup.*;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.jsp.JspFile;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.xdebugger.impl.DebuggerSupport;
+import com.intellij.xdebugger.impl.actions.EditBreakpointAction;
+import com.intellij.xdebugger.impl.actions.ViewBreakpointsAction;
+import com.intellij.xdebugger.impl.actions.XDebuggerActions;
+import com.intellij.xdebugger.ui.DebuggerColors;
+import com.intellij.xml.util.XmlStringUtil;
+import com.sun.jdi.ReferenceType;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.dnd.DragSource;
+
+/**
+ * User: lex
+ * Date: Sep 2, 2003
+ * Time: 3:22:55 PM
+ */
+public abstract class BreakpointWithHighlighter extends Breakpoint {
+ @Nullable private RangeHighlighter myHighlighter;
+
+ @Nullable private SourcePosition mySourcePosition;
+
+ private boolean myVisible = true;
+ private volatile Icon myIcon = getSetIcon(false);
+ @Nullable private String myClassName;
+ @Nullable private String myPackageName;
+ @Nullable private String myInvalidMessage;
+
+ protected abstract void createRequestForPreparedClass(final DebugProcessImpl debugProcess, final ReferenceType classType);
+
+ protected abstract Icon getDisabledIcon(boolean isMuted);
+
+ protected abstract Icon getInvalidIcon(boolean isMuted);
+
+ protected abstract Icon getSetIcon(boolean isMuted);
+
+ protected abstract Icon getVerifiedIcon(boolean isMuted);
+
+ protected abstract Icon getVerifiedWarningsIcon(boolean isMuted);
+
+ @Override
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ @Override
+ public String getClassName() {
+ return myClassName;
+ }
+
+ @Override
+ @Nullable
+ public String getShortClassName() {
+ final SourcePosition pos = getSourcePosition();
+ if (pos != null) {
+ if (pos.getFile() instanceof JspFile) {
+ return getClassName();
+ }
+ }
+ return super.getShortClassName();
+ }
+
+ @Override
+ public String getPackageName() {
+ return myPackageName;
+ }
+
+ @Nullable
+ protected Breakpoint init() {
+ if (!isValid()) {
+ myHighlighter.dispose();
+ return null;
+ }
+
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ updateUI();
+ updateGutter();
+ }
+
+ return this;
+ }
+
+ private void updateCaches(DebugProcessImpl debugProcess) {
+ myIcon = calcIcon(debugProcess);
+ myClassName = JVMNameUtil.getSourcePositionClassDisplayName(debugProcess, getSourcePosition());
+ myPackageName = JVMNameUtil.getSourcePositionPackageDisplayName(debugProcess, getSourcePosition());
+ }
+
+ private Icon calcIcon(@Nullable DebugProcessImpl debugProcess) {
+ final boolean muted = debugProcess != null && isMuted(debugProcess);
+ if (!ENABLED) {
+ return getDisabledIcon(muted);
+ }
+
+ myInvalidMessage = "";
+
+ if (!isValid()) {
+ return getInvalidIcon(muted);
+ }
+
+ if (debugProcess == null) {
+ return getSetIcon(muted);
+ }
+
+ final RequestManagerImpl requestsManager = debugProcess.getRequestsManager();
+
+ final boolean isVerified = myCachedVerifiedState || requestsManager.isVerified(this);
+
+ final String warning = requestsManager.getWarning(this);
+ if (warning != null) {
+ myInvalidMessage = warning;
+ if (!isVerified) {
+ return getInvalidIcon(muted);
+ }
+ return getVerifiedWarningsIcon(muted);
+ }
+
+ if (isVerified) {
+ return getVerifiedIcon(muted);
+ }
+
+ return getSetIcon(muted);
+ }
+
+ protected BreakpointWithHighlighter(@NotNull Project project) {
+ //for persistency
+ super(project);
+ }
+
+ public BreakpointWithHighlighter(@NotNull final Project project, @NotNull final RangeHighlighter highlighter) {
+ super(project);
+ myHighlighter = highlighter;
+ highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter());
+ reload();
+ }
+
+ public RangeHighlighter getHighlighter() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ return myHighlighter;
+ }
+
+ @Override
+ public boolean isValid() {
+ return isPositionValid(getSourcePosition());
+ }
+
+ private static boolean isPositionValid(@Nullable final SourcePosition sourcePosition) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+ @Override
+ public Boolean compute() {
+ return sourcePosition != null && sourcePosition.getFile().isValid() ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }).booleanValue();
+ }
+
+ public SourcePosition getSourcePosition() {
+ return mySourcePosition;
+ }
+
+ @SuppressWarnings("HardCodedStringLiteral")
+ @NotNull
+ public String getDescription() {
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ buf.append("<html><body>");
+ buf.append(getDisplayName());
+ if (myInvalidMessage != null && !myInvalidMessage.isEmpty()) {
+ buf.append("<br><font color='red'>");
+ buf.append(DebuggerBundle.message("breakpoint.warning", myInvalidMessage));
+ buf.append("</font>");
+ }
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.suspend.policy")).append(" : ");
+ if (DebuggerSettings.SUSPEND_ALL.equals(SUSPEND_POLICY)) {
+ buf.append(DebuggerBundle.message("breakpoint.properties.panel.option.suspend.all"));
+ }
+ else if (DebuggerSettings.SUSPEND_THREAD.equals(SUSPEND_POLICY)) {
+ buf.append(DebuggerBundle.message("breakpoint.properties.panel.option.suspend.thread"));
+ }
+ else if (DebuggerSettings.SUSPEND_NONE.equals(SUSPEND_POLICY)) {
+ buf.append(DebuggerBundle.message("breakpoint.properties.panel.option.suspend.none"));
+ }
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.log.message")).append(": ");
+ buf.append(LOG_ENABLED ? CommonBundle.getYesButtonText() : CommonBundle.getNoButtonText());
+ if (LOG_EXPRESSION_ENABLED) {
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.log.expression")).append(": ");
+ buf.append(XmlStringUtil.escapeString(getLogMessage().getText()));
+ }
+ if (CONDITION_ENABLED && getCondition() != null && getCondition().getText() != null && !getCondition().getText().isEmpty()) {
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.condition")).append(": ");
+ buf.append(XmlStringUtil.escapeString(getCondition().getText()));
+ }
+ if (COUNT_FILTER_ENABLED) {
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.pass.count")).append(": ");
+ buf.append(COUNT_FILTER);
+ }
+ if (CLASS_FILTERS_ENABLED) {
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.class.filters")).append(": ");
+ ClassFilter[] classFilters = getClassFilters();
+ for (ClassFilter classFilter : classFilters) {
+ buf.append(classFilter.getPattern()).append(" ");
+ }
+ }
+ if (INSTANCE_FILTERS_ENABLED) {
+ buf.append(" <br> ");
+ buf.append(DebuggerBundle.message("breakpoint.property.name.instance.filters"));
+ InstanceFilter[] instanceFilters = getInstanceFilters();
+ for (InstanceFilter instanceFilter : instanceFilters) {
+ buf.append(Long.toString(instanceFilter.getId())).append(" ");
+ }
+ }
+ buf.append("</body></html>");
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ @Override
+ public final void reload() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ if (getHighlighter().isValid()) {
+ PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(getHighlighter().getDocument());
+ if (psiFile != null) {
+ mySourcePosition = SourcePosition.createFromOffset(psiFile, getHighlighter().getStartOffset());
+ reload(psiFile);
+ return;
+ }
+ }
+ mySourcePosition = null;
+ }
+
+ @Override
+ public void createRequest(@NotNull DebugProcessImpl debugProcess) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ // check is this breakpoint is enabled, vm reference is valid and there're no requests created yet
+ if (!ENABLED ||
+ !debugProcess.isAttached() ||
+ isMuted(debugProcess) ||
+ !debugProcess.getRequestsManager().findRequests(this).isEmpty()) {
+ return;
+ }
+
+ if (!isValid()) {
+ return;
+ }
+
+ createOrWaitPrepare(debugProcess, getSourcePosition());
+ updateUI();
+ }
+
+ protected boolean isMuted(@NotNull final DebugProcessImpl debugProcess) {
+ return debugProcess.areBreakpointsMuted();
+ }
+
+ @Override
+ public void processClassPrepare(final DebugProcess debugProcess, final ReferenceType classType) {
+ if (!ENABLED || !isValid()) {
+ return;
+ }
+ createRequestForPreparedClass((DebugProcessImpl)debugProcess, classType);
+ updateUI();
+ }
+
+
+ /**
+ * updates the state of breakpoint and all the related UI widgets etc
+ */
+ @Override
+ public final void updateUI(@NotNull final Runnable afterUpdate) {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return;
+ }
+ final Project project = getProject();
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ @Override
+ public void run() {
+ if (!isValid()) {
+ return;
+ }
+
+ DebuggerContextImpl context = DebuggerManagerEx.getInstanceEx(project).getContext();
+ final DebugProcessImpl debugProcess = context.getDebugProcess();
+
+ if (debugProcess == null || !debugProcess.isAttached()) {
+ updateCaches(null);
+ updateGutter();
+ afterUpdate.run();
+ }
+ else {
+ debugProcess.getManagerThread().invoke(new DebuggerCommandImpl() {
+ @Override
+ protected void action() throws Exception {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ @Override
+ public void run() {
+ updateCaches(debugProcess);
+ }
+ });
+ DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
+ @Override
+ public void run() {
+ updateGutter();
+ afterUpdate.run();
+ }
+ });
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private void updateGutter() {
+ if (myVisible) {
+ RangeHighlighter highlighter = getHighlighter();
+ if (highlighter != null && highlighter.isValid() && isValid()) {
+ setupGutterRenderer(highlighter);
+ }
+ else {
+ DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().removeBreakpoint(this);
+ }
+ }
+ }
+
+ /**
+ * called by BreakpointManager when destroying the breakpoint
+ */
+ @Override
+ public void delete() {
+ if (isVisible()) {
+ final RangeHighlighter highlighter = getHighlighter();
+ if (highlighter != null) {
+ DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+ @Override
+ public void run() {
+ highlighter.dispose();
+ //we should delete it here, so gutter will not fire events to deleted breakpoint
+ BreakpointWithHighlighter.super.delete();
+ }
+ });
+ }
+ }
+
+ }
+
+ public boolean isAt(@NotNull Document document, int offset) {
+ RangeHighlighter highlighter = getHighlighter();
+ return highlighter != null &&
+ highlighter.isValid() &&
+ document.equals(highlighter.getDocument()) &&
+ getSourcePosition().getLine() == document.getLineNumber(offset);
+ }
+
+ protected void reload(PsiFile psiFile) {
+ }
+
+ @Override
+ public PsiClass getPsiClass() {
+ final SourcePosition sourcePosition = getSourcePosition();
+ return getPsiClassAt(sourcePosition);
+ }
+
+ protected static PsiClass getPsiClassAt(final SourcePosition sourcePosition) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
+ @Nullable
+ @Override
+ public PsiClass compute() {
+ return JVMNameUtil.getClassAt(sourcePosition);
+ }
+ });
+ }
+
+ private void setupGutterRenderer(@NotNull RangeHighlighter highlighter) {
+ MyGutterIconRenderer renderer = new MyGutterIconRenderer(getIcon(), getDescription());
+ highlighter.setGutterIconRenderer(renderer);
+ }
+
+ @Override
+ public abstract Key<? extends BreakpointWithHighlighter> getCategory();
+
+ public boolean canMoveTo(@Nullable final SourcePosition position) {
+ if (position == null || !position.getFile().isValid()) {
+ return false;
+ }
+ final PsiFile psiFile = position.getFile();
+ final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(psiFile);
+ if (document == null) {
+ return false;
+ }
+ final int spOffset = position.getOffset();
+ if (spOffset < 0) {
+ return false;
+ }
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
+ return breakpointManager.findBreakpoint(document, spOffset, getCategory()) == null;
+ }
+
+ public boolean moveTo(@NotNull SourcePosition position) {
+ if (!canMoveTo(position)) {
+ return false;
+ }
+ final PsiFile psiFile = position.getFile();
+ final PsiFile oldFile = getSourcePosition().getFile();
+ final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(psiFile);
+ final Document oldDocument = PsiDocumentManager.getInstance(getProject()).getDocument(oldFile);
+ if (document == null || oldDocument == null) {
+ return false;
+ }
+ final RangeHighlighter newHighlighter = createHighlighter(myProject, document, position.getLine());
+ if (newHighlighter == null) {
+ return false;
+ }
+ final RangeHighlighter oldHighlighter = myHighlighter;
+ myHighlighter = newHighlighter;
+
+ reload();
+
+ if (!isValid()) {
+ myHighlighter.dispose();
+ myHighlighter = oldHighlighter;
+ reload();
+ return false;
+ }
+
+ oldHighlighter.dispose();
+
+ DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager().fireBreakpointChanged(this);
+ updateUI();
+
+ return true;
+ }
+
+ public boolean isVisible() {
+ return myVisible;
+ }
+
+ public void setVisible(boolean visible) {
+ myVisible = visible;
+ }
+
+ @NotNull
+ public Document getDocument() {
+ return getHighlighter().getDocument();
+ }
+
+ public int getLineIndex() {
+ final SourcePosition sourcePosition = getSourcePosition();
+ return sourcePosition != null ? sourcePosition.getLine() : -1;
+ }
+
+ @Nullable
+ protected static RangeHighlighter createHighlighter(@NotNull Project project, @NotNull Document document, int lineIndex) {
+ if (lineIndex < 0 || lineIndex >= document.getLineCount()) {
+ return null;
+ }
+
+ EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
+ TextAttributes attributes = scheme.getAttributes(DebuggerColors.BREAKPOINT_ATTRIBUTES);
+
+ RangeHighlighter highlighter = ((MarkupModelEx)DocumentMarkupModel.forDocument(document, project, true))
+ .addPersistentLineHighlighter(lineIndex, DebuggerColors.BREAKPOINT_HIGHLIGHTER_LAYER, attributes);
+ if (!highlighter.isValid()) {
+ return null;
+ }
+ highlighter.putUserData(DebuggerColors.BREAKPOINT_HIGHLIGHTER_KEY, Boolean.TRUE);
+ highlighter.setErrorStripeTooltip(DebuggerBundle.message("breakpoint.tooltip.text", lineIndex + 1));
+ return highlighter;
+ }
+
+ @Override
+ public void readExternal(@NotNull Element breakpointNode) throws InvalidDataException {
+ super.readExternal(breakpointNode);
+ //noinspection HardCodedStringLiteral
+ final String url = breakpointNode.getAttributeValue("url");
+
+ //noinspection HardCodedStringLiteral
+ final String className = breakpointNode.getAttributeValue("class");
+ if (className != null) {
+ myClassName = className;
+ }
+
+ //noinspection HardCodedStringLiteral
+ final String packageName = breakpointNode.getAttributeValue("package");
+ if (packageName != null) {
+ myPackageName = packageName;
+ }
+
+ VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(url);
+ if (vFile == null) {
+ throw new InvalidDataException(DebuggerBundle.message("error.breakpoint.file.not.found", url));
+ }
+ final Document doc = FileDocumentManager.getInstance().getDocument(vFile);
+ if (doc == null) {
+ throw new InvalidDataException(DebuggerBundle.message("error.cannot.load.breakpoint.file", url));
+ }
+
+ // line number
+ final int line;
+ try {
+ //noinspection HardCodedStringLiteral
+ line = Integer.parseInt(breakpointNode.getAttributeValue("line"));
+ }
+ catch (Exception e) {
+ throw new InvalidDataException("Line number is invalid for breakpoint");
+ }
+ if (line < 0) {
+ throw new InvalidDataException("Line number is invalid for breakpoint");
+ }
+
+ RangeHighlighter highlighter = createHighlighter(myProject, doc, line);
+
+ if (highlighter == null) {
+ throw new InvalidDataException("");
+ }
+
+ myHighlighter = highlighter;
+ reload();
+ }
+
+ @Override
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(@NotNull Element parentNode) throws WriteExternalException {
+ super.writeExternal(parentNode);
+ PsiFile psiFile = getSourcePosition().getFile();
+ final VirtualFile virtualFile = psiFile.getVirtualFile();
+ final String url = virtualFile != null ? virtualFile.getUrl() : "";
+ parentNode.setAttribute("url", url);
+ parentNode.setAttribute("line", Integer.toString(getSourcePosition().getLine()));
+ if (myClassName != null) {
+ parentNode.setAttribute("class", myClassName);
+ }
+ if (myPackageName != null) {
+ parentNode.setAttribute("package", myPackageName);
+ }
+ }
+
+ private class MyGutterIconRenderer extends GutterIconRenderer {
+ private final Icon myIcon;
+ private final String myDescription;
+
+ public MyGutterIconRenderer(@NotNull Icon icon, @NotNull String description) {
+ myIcon = icon;
+ myDescription = description;
+ }
+
+ @Override
+ @NotNull
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ @Override
+ public String getTooltipText() {
+ return myDescription;
+ }
+
+ @Override
+ public AnAction getClickAction() {
+ return new AnAction() {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().removeBreakpoint(BreakpointWithHighlighter.this);
+ }
+ };
+ }
+
+ @Override
+ public AnAction getMiddleButtonClickAction() {
+ return new AnAction() {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ ENABLED = !ENABLED;
+ DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager().fireBreakpointChanged(BreakpointWithHighlighter.this);
+ updateUI();
+ }
+ };
+ }
+
+ @Override
+ public ActionGroup getPopupMenuActions() {
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+ /**
+ * Used from Popup Menu
+ */
+ class RemoveAction extends AnAction {
+ @Nullable private Breakpoint myBreakpoint;
+
+ public RemoveAction(Breakpoint breakpoint) {
+ super(DebuggerBundle.message("action.remove.text"));
+ myBreakpoint = breakpoint;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ if (myBreakpoint != null) {
+ breakpointManager.removeBreakpoint(myBreakpoint);
+ myBreakpoint = null;
+ }
+ }
+ }
+
+ /**
+ * Used from Popup Menu
+ */
+ class SetEnabledAction extends AnAction {
+ private final boolean myNewValue;
+ private final Breakpoint myBreakpoint;
+
+ public SetEnabledAction(Breakpoint breakpoint, boolean newValue) {
+ super(newValue ? DebuggerBundle.message("action.enable.text") : DebuggerBundle.message("action.disable.text"));
+ myBreakpoint = breakpoint;
+ myNewValue = newValue;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myBreakpoint.ENABLED = myNewValue;
+ breakpointManager.fireBreakpointChanged(myBreakpoint);
+ myBreakpoint.updateUI();
+ }
+ }
+
+
+ AnAction viewBreakpointsAction =
+ new ViewBreakpointsAction(ActionsBundle.actionText(XDebuggerActions.VIEW_BREAKPOINTS), BreakpointWithHighlighter.this);
+
+ DefaultActionGroup group = new DefaultActionGroup();
+ RangeHighlighter highlighter = getHighlighter();
+ if (highlighter != null) {
+ group.add(new EditBreakpointAction.ContextAction(this, BreakpointWithHighlighter.this, DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class)));
+ group.addSeparator();
+ }
+ group.add(new SetEnabledAction(BreakpointWithHighlighter.this, !ENABLED));
+ group.add(new RemoveAction(BreakpointWithHighlighter.this));
+ group.addSeparator();
+ group.add(viewBreakpointsAction);
+ return group;
+ }
+
+ @Override
+ public GutterDraggableObject getDraggableObject() {
+ return new GutterDraggableObject() {
+ @Override
+ public boolean copy(int line, @NotNull VirtualFile file) {
+ final PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(file);
+ return psiFile != null && moveTo(SourcePosition.createFromLine(psiFile, line));
+ }
+
+ @Override
+ public Cursor getCursor(int line) {
+ final SourcePosition newPosition = SourcePosition.createFromLine(getSourcePosition().getFile(), line);
+ return canMoveTo(newPosition) ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop;
+ }
+ };
+ }
+
+ @Override
+ public boolean equals(@NotNull Object obj) {
+ return obj instanceof MyGutterIconRenderer &&
+ Comparing.equal(getTooltipText(), ((MyGutterIconRenderer)obj).getTooltipText()) &&
+ Comparing.equal(getIcon(), ((MyGutterIconRenderer)obj).getIcon());
+ }
+
+ @Override
+ public int hashCode() {
+ return getIcon().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "LB " + getDisplayName();
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EditClassFiltersDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EditClassFiltersDialog.java
new file mode 100644
index 0000000..0f0de4a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EditClassFiltersDialog.java
@@ -0,0 +1,99 @@
+/*
+ * 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 EditClassFiltersDialog
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.ide.util.ClassFilter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.classFilter.ClassFilterEditor;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class EditClassFiltersDialog extends DialogWrapper {
+ private ClassFilterEditor myClassFilterEditor;
+ private ClassFilterEditor myClassExclusionFilterEditor;
+ private Project myProject;
+ private ClassFilter myChooserFilter;
+
+ public EditClassFiltersDialog(Project project) {
+ this(project, null);
+ }
+
+ public EditClassFiltersDialog(Project project, ClassFilter filter) {
+ super(project, true);
+ myChooserFilter = filter;
+ myProject = project;
+ setTitle(DebuggerBundle.message("class.filters.dialog.title"));
+ init();
+ }
+
+
+ protected JComponent createCenterPanel() {
+ JPanel contentPanel = new JPanel(new BorderLayout());
+
+ Box mainPanel = Box.createHorizontalBox();
+
+ myClassFilterEditor = new ClassFilterEditor(myProject, myChooserFilter, "reference.viewBreakpoints.classFilters.newPattern");
+ myClassFilterEditor.setPreferredSize(new Dimension(400, 200));
+ myClassFilterEditor.setBorder(IdeBorderFactory.createTitledBorder(
+ DebuggerBundle.message("class.filters.dialog.inclusion.filters.group"), false));
+ mainPanel.add(myClassFilterEditor);
+
+ myClassExclusionFilterEditor = new ClassFilterEditor(myProject, myChooserFilter, "reference.viewBreakpoints.classFilters.newPattern");
+ myClassExclusionFilterEditor.setPreferredSize(new Dimension(400, 200));
+ myClassExclusionFilterEditor.setBorder(IdeBorderFactory.createTitledBorder(
+ DebuggerBundle.message("class.filters.dialog.exclusion.filters.group"), false));
+ mainPanel.add(myClassExclusionFilterEditor);
+
+ contentPanel.add(mainPanel, BorderLayout.CENTER);
+
+ return contentPanel;
+ }
+
+ public void dispose(){
+ myClassFilterEditor.stopEditing();
+ super.dispose();
+ }
+
+ public void setFilters(com.intellij.ui.classFilter.ClassFilter[] filters, com.intellij.ui.classFilter.ClassFilter[] inverseFilters) {
+ myClassFilterEditor.setFilters(filters);
+ myClassExclusionFilterEditor.setFilters(inverseFilters);
+ }
+
+ protected String getDimensionServiceKey(){
+ return "#com.intellij.debugger.ui.breakpoints.EditClassFiltersDialog";
+ }
+
+ public com.intellij.ui.classFilter.ClassFilter[] getFilters() {
+ return myClassFilterEditor.getFilters();
+ }
+
+ public com.intellij.ui.classFilter.ClassFilter[] getExclusionFilters() {
+ return myClassExclusionFilterEditor.getFilters();
+ }
+
+ protected String getHelpId() {
+ return "reference.viewBreakpoints.classFilters";
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EditInstanceFiltersDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EditInstanceFiltersDialog.java
new file mode 100644
index 0000000..dbc2111
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EditInstanceFiltersDialog.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.debugger.InstanceFilter;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.ui.InstanceFilterEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.ui.IdeBorderFactory;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * User: lex
+ * Date: Aug 29, 2003
+ * Time: 2:44:38 PM
+ */
+public class EditInstanceFiltersDialog extends DialogWrapper{
+ private InstanceFilterEditor myInstanceFilterEditor;
+ private final Project myProject;
+
+ public EditInstanceFiltersDialog (Project project) {
+ super(project, true);
+ myProject = project;
+ setTitle(DebuggerBundle.message("instance.filters.dialog.title"));
+ init();
+ }
+
+ protected JComponent createCenterPanel() {
+ JPanel contentPanel = new JPanel(new BorderLayout());
+
+ Box mainPanel = Box.createHorizontalBox();
+
+ myInstanceFilterEditor = new InstanceFilterEditor(myProject);
+ myInstanceFilterEditor.setPreferredSize(new Dimension(400, 200));
+ myInstanceFilterEditor.setBorder(IdeBorderFactory.createTitledBorder(
+ DebuggerBundle.message("instance.filters.dialog.instance.filters.group"), false));
+ mainPanel.add(myInstanceFilterEditor);
+
+ contentPanel.add(mainPanel, BorderLayout.CENTER);
+
+ return contentPanel;
+ }
+
+ public void dispose(){
+ myInstanceFilterEditor.stopEditing();
+ super.dispose();
+ }
+
+ public void setFilters(InstanceFilter[] filters) {
+ ClassFilter[] cFilters = InstanceFilter.createClassFilters(filters);
+ myInstanceFilterEditor.setFilters(cFilters);
+ }
+
+ protected String getDimensionServiceKey(){
+ return "#com.intellij.debugger.ui.breakpoints.EditInstanceFiltersDialog";
+ }
+
+ public InstanceFilter[] getFilters() {
+ ClassFilter [] cFilters = myInstanceFilterEditor.getFilters();
+ InstanceFilter [] ifilters = new InstanceFilter[cFilters.length];
+ for (int i = 0; i < ifilters.length; i++) {
+ ifilters[i] = InstanceFilter.create(cFilters[i]);
+ }
+ return ifilters;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EnableBreakpointRule.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EnableBreakpointRule.java
new file mode 100644
index 0000000..bcda420
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/EnableBreakpointRule.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 10, 2005
+ */
+public class EnableBreakpointRule {
+ private final BreakpointManager myManager;
+ private final boolean myLeaveEnabled;
+ private final Breakpoint myMasterBreakpoint;
+ private final Breakpoint mySlaveBreakpoint;
+
+ public EnableBreakpointRule(@NotNull BreakpointManager manager, @NotNull Breakpoint masterBreakpoint, @NotNull Breakpoint slaveBreakpoint) {
+ this(manager,masterBreakpoint, slaveBreakpoint, false);
+ }
+
+ public EnableBreakpointRule(@NotNull BreakpointManager manager, @NotNull Breakpoint masterBreakpoint, @NotNull Breakpoint slaveBreakpoint, boolean leaveEnabled) {
+ myMasterBreakpoint = masterBreakpoint;
+ mySlaveBreakpoint = slaveBreakpoint;
+ myManager = manager;
+ myLeaveEnabled = leaveEnabled;
+ }
+
+ public Breakpoint getMasterBreakpoint() {
+ return myMasterBreakpoint;
+ }
+
+ public Breakpoint getSlaveBreakpoint() {
+ return mySlaveBreakpoint;
+ }
+
+ public boolean isLeaveEnabled() {
+ return myLeaveEnabled;
+ }
+
+ public void init() {
+ myManager.setBreakpointEnabled(getSlaveBreakpoint(), false);
+ }
+
+ public void dispose() {
+ myManager.setBreakpointEnabled(getSlaveBreakpoint(), true);
+ }
+
+ public void processBreakpointHit(Breakpoint breakpointHit) {
+ if (getMasterBreakpoint().equals(breakpointHit)) {
+ myManager.setBreakpointEnabled(getSlaveBreakpoint(), true);
+ }
+ else if (getSlaveBreakpoint().equals(breakpointHit)){
+ if (!myLeaveEnabled) {
+ myManager.setBreakpointEnabled(getSlaveBreakpoint(), false);
+ }
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpoint.java
new file mode 100644
index 0000000..c6a1955
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpoint.java
@@ -0,0 +1,250 @@
+/*
+ * 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 ExceptionBreakpoint
+ * @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.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.sun.jdi.AbsentInformationException;
+import com.sun.jdi.Location;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.event.ExceptionEvent;
+import com.sun.jdi.event.LocatableEvent;
+import com.sun.jdi.request.ExceptionRequest;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+
+public class ExceptionBreakpoint extends Breakpoint {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.ExceptionBreakpoint");
+
+ public boolean NOTIFY_CAUGHT = true;
+ public boolean NOTIFY_UNCAUGHT = true;
+ private String myQualifiedName;
+ private String myPackageName;
+
+ protected final static String READ_NO_CLASS_NAME = DebuggerBundle.message("error.absent.exception.breakpoint.class.name");
+ public static final @NonNls Key<ExceptionBreakpoint> CATEGORY = BreakpointCategory.lookup("exception_breakpoints");
+
+ public ExceptionBreakpoint(Project project) {
+ super(project);
+ }
+
+ public Key<? extends ExceptionBreakpoint> getCategory() {
+ return CATEGORY;
+ }
+
+ protected ExceptionBreakpoint(Project project, String qualifiedName, String packageName) {
+ super(project);
+ myQualifiedName = qualifiedName;
+ if (packageName == null) {
+ myPackageName = calcPackageName(qualifiedName);
+ }
+ else {
+ myPackageName = packageName;
+ }
+ }
+
+ private String calcPackageName(String qualifiedName) {
+ if (qualifiedName == null) {
+ return null;
+ }
+ int dotIndex = qualifiedName.lastIndexOf('.');
+ return dotIndex >= 0? qualifiedName.substring(0, dotIndex) : "";
+ }
+
+ public String getClassName() {
+ return myQualifiedName;
+ }
+
+ public String getPackageName() {
+ return myPackageName;
+ }
+
+ public PsiClass getPsiClass() {
+ return PsiDocumentManager.getInstance(myProject).commitAndRunReadAction(new Computable<PsiClass>() {
+ public PsiClass compute() {
+ return myQualifiedName != null ? DebuggerUtilsEx.findClass(myQualifiedName, myProject, GlobalSearchScope.allScope(myProject)) : null;
+ }
+ });
+ }
+
+ public String getDisplayName() {
+ return DebuggerBundle.message("breakpoint.exception.breakpoint.display.name", myQualifiedName);
+ }
+
+ public Icon getIcon() {
+ if (!ENABLED) {
+ final Breakpoint master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this);
+ return master == null? AllIcons.Debugger.Db_disabled_exception_breakpoint : AllIcons.Debugger.Db_dep_exception_breakpoint;
+ }
+ return AllIcons.Debugger.Db_exception_breakpoint;
+ }
+
+ public void reload() {
+ }
+
+ public void createRequest(final DebugProcessImpl debugProcess) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (!ENABLED || !debugProcess.isAttached() || debugProcess.areBreakpointsMuted() || !debugProcess.getRequestsManager().findRequests(this).isEmpty()) {
+ return;
+ }
+
+ SourcePosition classPosition = PsiDocumentManager.getInstance(myProject).commitAndRunReadAction(new Computable<SourcePosition>() {
+ public SourcePosition compute() {
+ PsiClass psiClass = DebuggerUtilsEx.findClass(myQualifiedName, myProject, debugProcess.getSearchScope());
+
+ return psiClass != null ? SourcePosition.createFromElement(psiClass) : null;
+ }
+ });
+
+ if(classPosition == null) {
+ createOrWaitPrepare(debugProcess, myQualifiedName);
+ }
+ else {
+ createOrWaitPrepare(debugProcess, classPosition);
+ }
+ }
+
+ public void processClassPrepare(DebugProcess process, ReferenceType refType) {
+ DebugProcessImpl debugProcess = (DebugProcessImpl)process;
+ if (!ENABLED) {
+ return;
+ }
+ // trying to create a request
+ ExceptionRequest request = debugProcess.getRequestsManager().createExceptionRequest(this, refType, NOTIFY_CAUGHT, NOTIFY_UNCAUGHT);
+ debugProcess.getRequestsManager().enableRequest(request);
+ if (LOG.isDebugEnabled()) {
+ if (refType != null) {
+ LOG.debug("Created exception request for reference type " + refType.name());
+ }
+ else {
+ LOG.debug("Created exception request for reference type null");
+ }
+ }
+ }
+
+ protected ObjectReference getThisObject(SuspendContextImpl context, LocatableEvent event) throws EvaluateException {
+ if(event instanceof ExceptionEvent) {
+ return ((ExceptionEvent) event).exception();
+ }
+ return super.getThisObject(context, event); //To change body of overriden methods use Options | File Templates.
+ }
+
+ public String getEventMessage(LocatableEvent event) {
+ String exceptionName = (myQualifiedName != null)? myQualifiedName : "java.lang.Throwable";
+ String threadName = null;
+ if (event instanceof ExceptionEvent) {
+ ExceptionEvent exceptionEvent = (ExceptionEvent)event;
+ try {
+ exceptionName = exceptionEvent.exception().type().name();
+ threadName = exceptionEvent.thread().name();
+ }
+ catch (Exception e) {
+ }
+ }
+ final Location location = event.location();
+ final String locationQName = location.declaringType().name() + "." + location.method().name();
+ String locationFileName = "";
+ try {
+ locationFileName = location.sourceName();
+ }
+ catch (AbsentInformationException e) {
+ locationFileName = "";
+ }
+ final int locationLine = Math.max(0, location.lineNumber());
+ if (threadName != null) {
+ return DebuggerBundle.message(
+ "exception.breakpoint.console.message.with.thread.info",
+ exceptionName,
+ threadName,
+ locationQName,
+ locationFileName,
+ locationLine
+ );
+ }
+ else {
+ return DebuggerBundle.message(
+ "exception.breakpoint.console.message",
+ exceptionName,
+ locationQName,
+ locationFileName,
+ locationLine
+ );
+ }
+ }
+
+ public boolean isValid() {
+ return true;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) public void writeExternal(Element parentNode) throws WriteExternalException {
+ super.writeExternal(parentNode);
+ if(myQualifiedName != null) {
+ parentNode.setAttribute("class_name", myQualifiedName);
+ }
+ if(myPackageName != null) {
+ parentNode.setAttribute("package_name", myPackageName);
+ }
+ }
+
+ public PsiElement getEvaluationElement() {
+ if (getClassName() == null) {
+ return null;
+ }
+ return JavaPsiFacade.getInstance(myProject).findClass(getClassName(), GlobalSearchScope.allScope(myProject));
+ }
+
+ public void readExternal(Element parentNode) throws InvalidDataException {
+ super.readExternal(parentNode);
+ //noinspection HardCodedStringLiteral
+ String className = parentNode.getAttributeValue("class_name");
+ myQualifiedName = className;
+ if(className == null) {
+ throw new InvalidDataException(READ_NO_CLASS_NAME);
+ }
+
+ //noinspection HardCodedStringLiteral
+ String packageName = parentNode.getAttributeValue("package_name");
+ myPackageName = packageName != null? packageName : calcPackageName(packageName);
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpointFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpointFactory.java
new file mode 100644
index 0000000..f1742f0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpointFactory.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.ui.breakpoints.actions.*;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.util.TreeClassChooser;
+import com.intellij.ide.util.TreeClassChooserFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jdom.Element;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 26, 2005
+ */
+public class ExceptionBreakpointFactory extends BreakpointFactory {
+ public Breakpoint createBreakpoint(Project project, final Element element) {
+ return new ExceptionBreakpoint(project);
+ }
+
+ public Icon getIcon() {
+ return AllIcons.Debugger.Db_exception_breakpoint;
+ }
+
+ public Icon getDisabledIcon() {
+ return AllIcons.Debugger.Db_disabled_exception_breakpoint;
+ }
+
+ @Override
+ protected String getHelpID() {
+ return HelpID.EXCEPTION_BREAKPOINTS;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return DebuggerBundle.message("exception.breakpoints.tab.title");
+ }
+
+ @Override
+ protected void configureBreakpointPanel(BreakpointPanel panel) {
+ super.configureBreakpointPanel(panel);
+ panel.getTree().setGroupByMethods(false);
+ }
+
+ @Override
+ public BreakpointPropertiesPanel createBreakpointPropertiesPanel(Project project, boolean compact) {
+ return new ExceptionBreakpointPropertiesPanel(project, compact);
+ }
+
+ @Override
+ protected BreakpointPanelAction[] createBreakpointPanelActions(final Project project, DialogWrapper parentDialog) {
+ return new BreakpointPanelAction[]{
+ new SwitchViewAction(),
+ new AddAction(this, project),
+ new RemoveAction(project) {
+ public void update() {
+ super.update();
+ if (getButton().isEnabled()) {
+ Breakpoint[] selectedBreakpoints = getPanel().getSelectedBreakpoints();
+ for (Breakpoint bp : selectedBreakpoints) {
+ if (bp instanceof AnyExceptionBreakpoint) {
+ getButton().setEnabled(false);
+ }
+ }
+ }
+ }
+ }, new ToggleGroupByClassesAction(), new ToggleFlattenPackagesAction(),};
+ }
+
+ public BreakpointPanel createBreakpointPanel(final Project project, final DialogWrapper parentDialog) {
+ BreakpointPanel panel =
+ new BreakpointPanel(project, createBreakpointPropertiesPanel(project, false), createBreakpointPanelActions(project, parentDialog),
+ getBreakpointCategory(), getDisplayName(), getHelpID()) {
+ public void resetBreakpoints() {
+ super.resetBreakpoints();
+ Breakpoint[] breakpoints = getBreakpointManager().getBreakpoints(getBreakpointCategory());
+ final AnyExceptionBreakpoint anyExceptionBreakpoint =
+ DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().getAnyExceptionBreakpoint();
+ boolean found = false;
+ for (Breakpoint breakpoint : breakpoints) {
+ if (breakpoint.equals(anyExceptionBreakpoint)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ insertBreakpointAt(anyExceptionBreakpoint, 0);
+ }
+ }
+ };
+ configureBreakpointPanel(panel);
+ return panel;
+ }
+
+ public Key<ExceptionBreakpoint> getBreakpointCategory() {
+ return ExceptionBreakpoint.CATEGORY;
+ }
+
+ @Override
+ public boolean canAddBreakpoints() {
+ return true;
+ }
+
+ @Override
+ public Breakpoint addBreakpoint(Project project) {
+ ExceptionBreakpoint breakpoint = null;
+ final PsiClass throwableClass =
+ JavaPsiFacade.getInstance(project).findClass("java.lang.Throwable", GlobalSearchScope.allScope(project));
+ TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project)
+ .createInheritanceClassChooser(DebuggerBundle.message("add.exception.breakpoint.classchooser.title"),
+ GlobalSearchScope.allScope(project), throwableClass, true, true, null);
+ chooser.showDialog();
+ PsiClass selectedClass = chooser.getSelected();
+ String qName = selectedClass == null ? null : JVMNameUtil.getNonAnonymousClassName(selectedClass);
+
+ if (qName != null && qName.length() > 0) {
+ breakpoint = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager()
+ .addExceptionBreakpoint(qName, ((PsiClassOwner)selectedClass.getContainingFile()).getPackageName());
+ }
+ return breakpoint;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpointPropertiesPanel.java
new file mode 100644
index 0000000..73680f5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/ExceptionBreakpointPropertiesPanel.java
@@ -0,0 +1,116 @@
+/*
+ * 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 ExceptionBreakpointPropertiesPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.ide.util.ClassFilter;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.ui.DialogUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class ExceptionBreakpointPropertiesPanel extends BreakpointPropertiesPanel {
+ private JCheckBox myNotifyCaughtCheckBox;
+ private JCheckBox myNotifyUncaughtCheckBox;
+ private ExceptionBreakpoint myExceptionBreakpoint;
+
+ public ExceptionBreakpointPropertiesPanel(Project project, boolean compact) {
+ super(project, ExceptionBreakpoint.CATEGORY, compact);
+ }
+
+ protected ClassFilter createClassConditionFilter() {
+ return null;
+ }
+
+ protected JComponent createSpecialBox() {
+
+ myNotifyCaughtCheckBox = new JCheckBox(DebuggerBundle.message("label.exception.breakpoint.properties.panel.caught.exception"));
+ myNotifyUncaughtCheckBox = new JCheckBox(DebuggerBundle.message("label.exception.breakpoint.properties.panel.uncaught.exception"));
+ DialogUtil.registerMnemonic(myNotifyCaughtCheckBox);
+ DialogUtil.registerMnemonic(myNotifyUncaughtCheckBox);
+
+
+ Box notificationsBox = Box.createVerticalBox();
+ JPanel _panel = new JPanel(new BorderLayout());
+ _panel.add(myNotifyCaughtCheckBox, BorderLayout.NORTH);
+ notificationsBox.add(_panel);
+ _panel = new JPanel(new BorderLayout());
+ _panel.add(myNotifyUncaughtCheckBox, BorderLayout.NORTH);
+ notificationsBox.add(_panel);
+
+ _panel = new JPanel(new BorderLayout());
+ JPanel _panel0 = new JPanel(new BorderLayout());
+ _panel0.add(notificationsBox, BorderLayout.CENTER);
+ _panel0.add(Box.createHorizontalStrut(3), BorderLayout.WEST);
+ _panel0.add(Box.createHorizontalStrut(3), BorderLayout.EAST);
+ _panel.add(_panel0, BorderLayout.NORTH);
+ _panel.setBorder(IdeBorderFactory.createTitledBorder(
+ DebuggerBundle.message("label.exception.breakpoint.properties.panel.group.notifications"), true));
+
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (!myNotifyCaughtCheckBox.isSelected() && !myNotifyUncaughtCheckBox.isSelected()) {
+ Object source = e.getSource();
+ JCheckBox toCheck = null;
+ if (myNotifyCaughtCheckBox.equals(source)) {
+ toCheck = myNotifyUncaughtCheckBox;
+ }
+ else if (myNotifyUncaughtCheckBox.equals(source)) {
+ toCheck = myNotifyCaughtCheckBox;
+ }
+ if (toCheck != null) {
+ toCheck.setSelected(true);
+ }
+ }
+ }
+ };
+ myNotifyCaughtCheckBox.addActionListener(listener);
+ myNotifyUncaughtCheckBox.addActionListener(listener);
+ return _panel;
+ }
+
+ protected void updateCheckboxes() {
+ super.updateCheckboxes();
+ myPassCountCheckbox.setEnabled(!(myExceptionBreakpoint instanceof AnyExceptionBreakpoint));
+ }
+
+ public void initFrom(Breakpoint breakpoint, boolean moreOptionsVisible) {
+ ExceptionBreakpoint exceptionBreakpoint = (ExceptionBreakpoint)breakpoint;
+ myExceptionBreakpoint = exceptionBreakpoint;
+ super.initFrom(breakpoint, moreOptionsVisible);
+
+ myNotifyCaughtCheckBox.setSelected(exceptionBreakpoint.NOTIFY_CAUGHT);
+ myNotifyUncaughtCheckBox.setSelected(exceptionBreakpoint.NOTIFY_UNCAUGHT);
+ }
+
+ public void saveTo(Breakpoint breakpoint, @NotNull Runnable afterUpdate) {
+ ExceptionBreakpoint exceptionBreakpoint = (ExceptionBreakpoint)breakpoint;
+ exceptionBreakpoint.NOTIFY_CAUGHT = myNotifyCaughtCheckBox.isSelected();
+ exceptionBreakpoint.NOTIFY_UNCAUGHT = myNotifyUncaughtCheckBox.isSelected();
+
+ super.saveTo(breakpoint, afterUpdate);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpoint.java
new file mode 100644
index 0000000..365ae24
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpoint.java
@@ -0,0 +1,393 @@
+/*
+ * 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.editor.markup.RangeHighlighter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+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.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 javax.swing.*;
+import java.util.List;
+
+public class FieldBreakpoint extends BreakpointWithHighlighter {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.FieldBreakpoint");
+ public boolean WATCH_MODIFICATION = true;
+ public boolean WATCH_ACCESS = false;
+ private boolean myIsStatic;
+ private String myFieldName;
+
+ @NonNls public static final Key<FieldBreakpoint> CATEGORY = BreakpointCategory.lookup("field_breakpoints");
+
+ protected FieldBreakpoint(Project project) {
+ super(project);
+ }
+
+ private FieldBreakpoint(Project project, RangeHighlighter highlighter, @NotNull String fieldName) {
+ super(project, highlighter);
+ myFieldName = fieldName;
+ }
+
+ public boolean isStatic() {
+ return myIsStatic;
+ }
+
+ public String getFieldName() {
+ return 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(myFieldName, 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) {
+ myFieldName = field.getName();
+ myIsStatic = field.hasModifierProperty(PsiModifier.STATIC);
+ }
+ if (myIsStatic) {
+ INSTANCE_FILTERS_ENABLED = 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(myFieldName);
+ if (field == null) {
+ debugProcess.getRequestsManager().setInvalid(this, DebuggerBundle.message("error.invalid.breakpoint.missing.field.in.class", myFieldName, refType.name()));
+ return;
+ }
+ RequestManagerImpl manager = debugProcess.getRequestsManager();
+ if (WATCH_MODIFICATION && vm.canWatchFieldModification()) {
+ ModificationWatchpointRequest request = manager.createModificationWatchpointRequest(this, field);
+ debugProcess.getRequestsManager().enableRequest(request);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Modification request added");
+ }
+ }
+ if (WATCH_ACCESS && 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 = getSourcePosition().getFile().getName();
+ }
+ 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 + "." + myFieldName : myFieldName;
+ }
+
+ public static FieldBreakpoint create(@NotNull Project project, @NotNull Document document, int lineIndex, String fieldName) {
+ FieldBreakpoint breakpoint = new FieldBreakpoint(project, createHighlighter(project, document, lineIndex), fieldName);
+ 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) {
+ 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);
+ 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
+ myFieldName = breakpointNode.getAttributeValue("field_name");
+ if(myFieldName == null) {
+ throw new InvalidDataException("No field name for field breakpoint");
+ }
+ }
+
+ @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();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpointFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpointFactory.java
new file mode 100644
index 0000000..496e013
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpointFactory.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.CommonBundle;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.ui.breakpoints.actions.*;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Ref;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jdom.Element;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 26, 2005
+ */
+public class FieldBreakpointFactory extends BreakpointFactory{
+ public Breakpoint createBreakpoint(Project project, final Element element) {
+ return new FieldBreakpoint(project);
+ }
+
+ public Icon getIcon() {
+ return AllIcons.Debugger.Db_field_breakpoint;
+ }
+
+ public Icon getDisabledIcon() {
+ return AllIcons.Debugger.Db_disabled_field_breakpoint;
+ }
+
+ @Override
+ protected void configureBreakpointPanel(BreakpointPanel panel) {
+ panel.getTree().setGroupByMethods(false);
+ }
+
+ @Override
+ protected String getHelpID() {
+ return HelpID.FIELD_WATCHPOINTS;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return DebuggerBundle.message("field.watchpoints.tab.title");
+ }
+
+ @Override
+ public BreakpointPropertiesPanel createBreakpointPropertiesPanel(Project project, boolean compact) {
+ return new FieldBreakpointPropertiesPanel(project, compact);
+ }
+
+ @Override
+ protected BreakpointPanelAction[] createBreakpointPanelActions(final Project project, final DialogWrapper parentDialog) {
+ return new BreakpointPanelAction[] {
+ new SwitchViewAction(),
+ new AddAction(this, project),
+ new GotoSourceAction(project) {
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ parentDialog.close(DialogWrapper.OK_EXIT_CODE);
+ }
+ },
+ new ViewSourceAction(project),
+ new RemoveAction(project),
+ new ToggleGroupByClassesAction(),
+ new ToggleFlattenPackagesAction(),
+ };
+ }
+
+ public Key<FieldBreakpoint> getBreakpointCategory() {
+ return FieldBreakpoint.CATEGORY;
+ }
+
+ @Override
+ public Breakpoint addBreakpoint(final Project project) {
+ final Ref<Breakpoint> result = Ref.create(null);
+ AddFieldBreakpointDialog dialog = new AddFieldBreakpointDialog(project) {
+ protected boolean validateData() {
+ String className = getClassName();
+ if (className.length() == 0) {
+ Messages.showMessageDialog(project, DebuggerBundle.message("error.field.breakpoint.class.name.not.specified"),
+ DebuggerBundle.message("add.field.breakpoint.dialog.title"), Messages.getErrorIcon());
+ return false;
+ }
+ String fieldName = getFieldName();
+ if (fieldName.length() == 0) {
+ Messages.showMessageDialog(project, DebuggerBundle.message("error.field.breakpoint.field.name.not.specified"),
+ DebuggerBundle.message("add.field.breakpoint.dialog.title"), Messages.getErrorIcon());
+ return false;
+ }
+ PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
+ if (psiClass != null) {
+ PsiFile psiFile = psiClass.getContainingFile();
+ Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
+ if(document != null) {
+ PsiField field = psiClass.findFieldByName(fieldName, true);
+ if(field != null) {
+ int line = document.getLineNumber(field.getTextOffset());
+ FieldBreakpoint fieldBreakpoint = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().addFieldBreakpoint(document, line, fieldName);
+ if (fieldBreakpoint != null) {
+ result.set(fieldBreakpoint);
+ return true;
+ }
+ }
+ else {
+ Messages.showMessageDialog(project,
+ DebuggerBundle.message("error.field.breakpoint.field.not.found", className, fieldName, fieldName),
+ CommonBundle.getErrorTitle(),
+ Messages.getErrorIcon()
+ );
+ }
+ }
+ } else {
+ Messages.showMessageDialog(project,
+ DebuggerBundle.message("error.field.breakpoint.class.sources.not.found", className, fieldName, className),
+ CommonBundle.getErrorTitle(),
+ Messages.getErrorIcon()
+ );
+ }
+ return false;
+ }
+ };
+ dialog.show();
+ return result.get();
+ }
+
+ @Override
+ public boolean canAddBreakpoints() {
+ return true;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpointPropertiesPanel.java
new file mode 100644
index 0000000..77497f7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FieldBreakpointPropertiesPanel.java
@@ -0,0 +1,106 @@
+/*
+ * 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 FieldBreakpointPropertiesPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.ui.DialogUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class FieldBreakpointPropertiesPanel extends BreakpointPropertiesPanel {
+ private JCheckBox myWatchAccessCheckBox;
+ private JCheckBox myWatchModificationCheckBox;
+
+ public FieldBreakpointPropertiesPanel(final Project project, boolean compact) {
+ super(project, FieldBreakpoint.CATEGORY, compact);
+ }
+
+ protected JComponent createSpecialBox() {
+ JPanel _panel;
+ JPanel _panel0;
+ myWatchAccessCheckBox = new JCheckBox(DebuggerBundle.message("label.filed.breakpoint.properties.panel.field.access"));
+ myWatchModificationCheckBox = new JCheckBox(DebuggerBundle.message("label.filed.breakpoint.properties.panel.field.modification"));
+ DialogUtil.registerMnemonic(myWatchAccessCheckBox);
+ DialogUtil.registerMnemonic(myWatchModificationCheckBox);
+
+
+ Box watchBox = Box.createVerticalBox();
+ _panel = new JPanel(new BorderLayout());
+ _panel.add(myWatchAccessCheckBox, BorderLayout.NORTH);
+ watchBox.add(_panel);
+ _panel = new JPanel(new BorderLayout());
+ _panel.add(myWatchModificationCheckBox, BorderLayout.NORTH);
+ watchBox.add(_panel);
+
+ _panel = new JPanel(new BorderLayout());
+ _panel0 = new JPanel(new BorderLayout());
+ _panel0.add(watchBox, BorderLayout.CENTER);
+ _panel0.add(Box.createHorizontalStrut(3), BorderLayout.WEST);
+ _panel0.add(Box.createHorizontalStrut(3), BorderLayout.EAST);
+ _panel.add(_panel0, BorderLayout.NORTH);
+ _panel.setBorder(IdeBorderFactory.createTitledBorder(DebuggerBundle.message("label.group.watch.events"), true));
+
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JCheckBox toCheck = null;
+ if (!myWatchAccessCheckBox.isSelected() && !myWatchModificationCheckBox.isSelected()) {
+ Object source = e.getSource();
+ if (myWatchAccessCheckBox.equals(source)) {
+ toCheck = myWatchModificationCheckBox;
+ }
+ else if (myWatchModificationCheckBox.equals(source)) {
+ toCheck = myWatchAccessCheckBox;
+ }
+ if (toCheck != null) {
+ toCheck.setSelected(true);
+ }
+ }
+ }
+ };
+ myWatchAccessCheckBox.addActionListener(listener);
+ myWatchModificationCheckBox.addActionListener(listener);
+
+ return _panel;
+ }
+
+ public void initFrom(Breakpoint breakpoint, boolean moreOptionsVisible) {
+ super.initFrom(breakpoint, moreOptionsVisible);
+ FieldBreakpoint fieldBreakpoint = (FieldBreakpoint)breakpoint;
+
+ myWatchAccessCheckBox.setSelected(fieldBreakpoint.WATCH_ACCESS);
+ myWatchModificationCheckBox.setSelected(fieldBreakpoint.WATCH_MODIFICATION);
+ }
+
+ public void saveTo(Breakpoint breakpoint, @NotNull Runnable afterUpdate) {
+ FieldBreakpoint fieldBreakpoint = (FieldBreakpoint)breakpoint;
+
+ fieldBreakpoint.WATCH_ACCESS = myWatchAccessCheckBox.isSelected();
+ fieldBreakpoint.WATCH_MODIFICATION = myWatchModificationCheckBox.isSelected();
+
+ super.saveTo(breakpoint, afterUpdate);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FilteredRequestor.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FilteredRequestor.java
new file mode 100644
index 0000000..e8fd23e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/FilteredRequestor.java
@@ -0,0 +1,227 @@
+/*
+ * 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 FilteredRequestor
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.*;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.engine.requests.LocatableEventRequestor;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.Value;
+import com.sun.jdi.event.LocatableEvent;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class FilteredRequestor implements LocatableEventRequestor, JDOMExternalizable {
+
+ public String SUSPEND_POLICY = DebuggerSettings.SUSPEND_ALL;
+ public boolean SUSPEND = true;
+
+ public boolean COUNT_FILTER_ENABLED = false;
+ public int COUNT_FILTER = 0;
+
+ public boolean CONDITION_ENABLED = false;
+ private TextWithImports myCondition;
+
+ public boolean CLASS_FILTERS_ENABLED = false;
+ private ClassFilter[] myClassFilters = ClassFilter.EMPTY_ARRAY;
+ private ClassFilter[] myClassExclusionFilters = ClassFilter.EMPTY_ARRAY;
+
+ public boolean INSTANCE_FILTERS_ENABLED = false;
+ private InstanceFilter[] myInstanceFilters = InstanceFilter.EMPTY_ARRAY;
+
+ @NonNls private static final String FILTER_OPTION_NAME = "filter";
+ @NonNls private static final String EXCLUSION_FILTER_OPTION_NAME = "exclusion_filter";
+ @NonNls private static final String INSTANCE_ID_OPTION_NAME = "instance_id";
+ @NonNls private static final String CONDITION_OPTION_NAME = "CONDITION";
+ protected final Project myProject;
+
+ public FilteredRequestor(@NotNull Project project) {
+ myProject = project;
+ myCondition = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, "");
+ }
+
+ public InstanceFilter[] getInstanceFilters() {
+ return myInstanceFilters;
+ }
+
+ public void setInstanceFilters(InstanceFilter[] instanceFilters) {
+ myInstanceFilters = instanceFilters != null? instanceFilters : InstanceFilter.EMPTY_ARRAY;
+ }
+
+ public String getSuspendPolicy() {
+ return SUSPEND? SUSPEND_POLICY : DebuggerSettings.SUSPEND_NONE;
+ }
+
+ /**
+ * @return true if the ID was added or false otherwise
+ */
+ private boolean hasObjectID(long id) {
+ for (InstanceFilter instanceFilter : myInstanceFilters) {
+ if (instanceFilter.getId() == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void addInstanceFilter(long l) {
+ final InstanceFilter[] filters = new InstanceFilter[myInstanceFilters.length + 1];
+ System.arraycopy(myInstanceFilters, 0, filters, 0, myInstanceFilters.length);
+ filters[myInstanceFilters.length] = InstanceFilter.create(String.valueOf(l));
+ myInstanceFilters = filters;
+ }
+
+ public final ClassFilter[] getClassFilters() {
+ return myClassFilters;
+ }
+
+ public final void setClassFilters(ClassFilter[] classFilters) {
+ myClassFilters = classFilters != null? classFilters : ClassFilter.EMPTY_ARRAY;
+ }
+
+ public ClassFilter[] getClassExclusionFilters() {
+ return myClassExclusionFilters;
+ }
+
+ public void setClassExclusionFilters(ClassFilter[] classExclusionFilters) {
+ myClassExclusionFilters = classExclusionFilters != null? classExclusionFilters : ClassFilter.EMPTY_ARRAY;
+ }
+
+ public void readExternal(Element parentNode) throws InvalidDataException {
+ DefaultJDOMExternalizer.readExternal(this, parentNode);
+ if (DebuggerSettings.SUSPEND_NONE.equals(SUSPEND_POLICY)) { // compatibility with older format
+ SUSPEND = false;
+ SUSPEND_POLICY = DebuggerSettings.SUSPEND_ALL;
+ }
+ String condition = JDOMExternalizerUtil.readField(parentNode, CONDITION_OPTION_NAME);
+ if (condition != null) {
+ setCondition(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, condition));
+ }
+
+ myClassFilters = DebuggerUtilsEx.readFilters(parentNode.getChildren(FILTER_OPTION_NAME));
+ myClassExclusionFilters = DebuggerUtilsEx.readFilters(parentNode.getChildren(EXCLUSION_FILTER_OPTION_NAME));
+
+ final ClassFilter [] instanceFilters = DebuggerUtilsEx.readFilters(parentNode.getChildren(INSTANCE_ID_OPTION_NAME));
+ final List<InstanceFilter> iFilters = new ArrayList<InstanceFilter>(instanceFilters.length);
+
+ for (ClassFilter instanceFilter : instanceFilters) {
+ try {
+ iFilters.add(InstanceFilter.create(instanceFilter));
+ }
+ catch (Exception e) {
+ }
+ }
+ myInstanceFilters = iFilters.isEmpty() ? InstanceFilter.EMPTY_ARRAY : iFilters.toArray(new InstanceFilter[iFilters.size()]);
+ }
+
+ public void writeExternal(Element parentNode) throws WriteExternalException {
+ DefaultJDOMExternalizer.writeExternal(this, parentNode);
+ JDOMExternalizerUtil.writeField(parentNode, CONDITION_OPTION_NAME, getCondition().toExternalForm());
+ DebuggerUtilsEx.writeFilters(parentNode, FILTER_OPTION_NAME, myClassFilters);
+ DebuggerUtilsEx.writeFilters(parentNode, EXCLUSION_FILTER_OPTION_NAME, myClassExclusionFilters);
+ DebuggerUtilsEx.writeFilters(parentNode, INSTANCE_ID_OPTION_NAME, InstanceFilter.createClassFilters(myInstanceFilters));
+ }
+
+ public boolean evaluateCondition(final EvaluationContextImpl context, LocatableEvent event) throws EvaluateException {
+ if(COUNT_FILTER_ENABLED) {
+ final DebugProcessImpl debugProcess = context.getDebugProcess();
+ debugProcess.getVirtualMachineProxy().suspend();
+ debugProcess.getRequestsManager().deleteRequest(this);
+ ((Breakpoint)this).createRequest(debugProcess);
+ debugProcess.getVirtualMachineProxy().resume();
+ }
+ if (INSTANCE_FILTERS_ENABLED) {
+ Value value = context.getThisObject();
+ if (value != null) { // non-static
+ ObjectReference reference = (ObjectReference)value;
+ if(!hasObjectID(reference.uniqueID())) {
+ return false;
+ }
+ }
+ }
+
+ if (CONDITION_ENABLED && getCondition() != null && !"".equals(getCondition().getText())) {
+ try {
+ ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(context.getProject(), new EvaluatingComputable<ExpressionEvaluator>() {
+ public ExpressionEvaluator compute() throws EvaluateException {
+ final SourcePosition contextSourcePosition = ContextUtil.getSourcePosition(context);
+ // IMPORTANT: calculate context psi element basing on the location where the exception
+ // has been hit, not on the location where it was set. (For line breakpoints these locations are the same, however,
+ // for method, exception and field breakpoints these locations differ)
+ PsiElement contextPsiElement = ContextUtil.getContextElement(contextSourcePosition);
+ if (contextPsiElement == null) {
+ contextPsiElement = getEvaluationElement(); // as a last resort
+ }
+ return EvaluatorBuilderImpl.build(getCondition(), contextPsiElement, contextSourcePosition);
+ }
+ });
+ final Value value = evaluator.evaluate(context);
+ if (!(value instanceof BooleanValue)) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.boolean.expected"));
+ }
+ if(!((BooleanValue)value).booleanValue()) {
+ return false;
+ }
+ }
+ catch (EvaluateException ex) {
+ if(ex.getCause() instanceof VMDisconnectedException) {
+ return false;
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("error.failed.evaluating.breakpoint.condition", getCondition(), ex.getMessage())
+ );
+ }
+ return true;
+ }
+
+ return true;
+ }
+
+ public abstract PsiElement getEvaluationElement();
+
+ public TextWithImports getCondition() {
+ return myCondition;
+ }
+
+ public void setCondition(TextWithImports condition) {
+ myCondition = condition;
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java
new file mode 100644
index 0000000..d37ae54
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.SimpleColoredComponent;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.popup.util.DetailView;
+import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+class JavaBreakpointItem extends BreakpointItem {
+ private final Breakpoint myBreakpoint;
+ private BreakpointFactory myBreakpointFactory;
+
+ public JavaBreakpointItem(@Nullable BreakpointFactory breakpointFactory, Breakpoint breakpoint) {
+ myBreakpointFactory = breakpointFactory;
+ myBreakpoint = breakpoint;
+ }
+
+ @Override
+ protected void setupGenericRenderer(SimpleColoredComponent renderer, boolean plainView) {
+ if (plainView) {
+ renderer.setIcon(myBreakpoint.getIcon());
+ }
+ renderer.append(plainView ? StringUtil.shortenTextWithEllipsis(myBreakpoint.getShortName(), 60, 0) : myBreakpoint.getDisplayName(),
+ isEnabled() ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES);
+ }
+
+ @Override
+ public Icon getIcon() {
+ return myBreakpoint.getIcon();
+ }
+
+ @Override
+ public String getDisplayText() {
+ return myBreakpoint.getDisplayName();
+ }
+
+ @Override
+ public String speedSearchText() {
+ return myBreakpoint.getDisplayName();
+ }
+
+ @Override
+ public String footerText() {
+ return myBreakpoint.getDisplayName();
+ }
+
+ @Override
+ protected void doUpdateDetailView(DetailView panel, boolean editorOnly) {
+ BreakpointPropertiesPanel breakpointPropertiesPanel = null;
+ if (!editorOnly) {
+ breakpointPropertiesPanel = myBreakpointFactory != null ? myBreakpointFactory
+ .createBreakpointPropertiesPanel(myBreakpoint.getProject(), false) : null;
+
+ if (breakpointPropertiesPanel != null) {
+ breakpointPropertiesPanel.initFrom(myBreakpoint, true);
+
+ breakpointPropertiesPanel.setSaveOnRemove(true);
+
+ final JPanel mainPanel = breakpointPropertiesPanel.getPanel();
+ panel.setPropertiesPanel(mainPanel);
+ }
+ else {
+ panel.setPropertiesPanel(null);
+ }
+ }
+
+ if (myBreakpoint instanceof BreakpointWithHighlighter) {
+ SourcePosition sourcePosition = ((BreakpointWithHighlighter)myBreakpoint).getSourcePosition();
+ VirtualFile virtualFile = sourcePosition.getFile().getVirtualFile();
+ showInEditor(panel, virtualFile, sourcePosition.getLine());
+ } else {
+ panel.clearEditor();
+ }
+ if (breakpointPropertiesPanel != null) {
+ breakpointPropertiesPanel.setDetailView(panel);
+ }
+ }
+
+ @Override
+ public boolean navigate() {
+ if (myBreakpoint instanceof BreakpointWithHighlighter) {
+ ((BreakpointWithHighlighter)myBreakpoint).getSourcePosition().navigate(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean allowedToRemove() {
+ return myBreakpointFactory != null && myBreakpointFactory.breakpointCanBeRemoved(myBreakpoint);
+ }
+
+ @Override
+ public void removed(Project project) {
+ DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().removeBreakpoint(myBreakpoint);
+ }
+
+ @Override
+ public Object getBreakpoint() {
+ return myBreakpoint;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return myBreakpoint.ENABLED;
+ }
+
+ @Override
+ public void setEnabled(boolean state) {
+ myBreakpoint.ENABLED = state;
+ }
+
+ @Override
+ public boolean isDefaultBreakpoint() {
+ return myBreakpoint.getCategory().equals(AnyExceptionBreakpoint.CATEGORY);
+ }
+
+ @Override
+ public int compareTo(BreakpointItem breakpointItem) {
+ final Object breakpoint = breakpointItem.getBreakpoint();
+ if (breakpoint instanceof Breakpoint) {
+ return -getIndexOf(myBreakpoint) + getIndexOf((Breakpoint)breakpoint);
+ }
+ return getDisplayText().compareTo(breakpointItem.getDisplayText());
+ }
+
+ private int getIndexOf(Breakpoint breakpoint) {
+ return DebuggerManagerEx.getInstanceEx(myBreakpoint.getProject()).getBreakpointManager().getBreakpoints().indexOf(breakpoint);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java
new file mode 100644
index 0000000..de3b918
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java
@@ -0,0 +1,464 @@
+/*
+ * 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 LineBreakpoint
+ * @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.actions.ThreadDumpAction;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+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.editor.markup.RangeHighlighter;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.jsp.JspFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.util.Processor;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.xdebugger.XDebuggerUtil;
+import com.sun.jdi.*;
+import com.sun.jdi.event.LocatableEvent;
+import com.sun.jdi.request.BreakpointRequest;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class LineBreakpoint extends BreakpointWithHighlighter {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.LineBreakpoint");
+
+ private String myMethodName;
+ public static final @NonNls Key<LineBreakpoint> CATEGORY = BreakpointCategory.lookup("line_breakpoints");
+
+ protected LineBreakpoint(Project project) {
+ super(project);
+ }
+
+ protected LineBreakpoint(Project project, RangeHighlighter highlighter) {
+ super(project, highlighter);
+ }
+
+ protected Icon getDisabledIcon(boolean isMuted) {
+ final Breakpoint master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this);
+ if (isMuted) {
+ return master == null? AllIcons.Debugger.Db_muted_disabled_breakpoint : AllIcons.Debugger.Db_muted_dep_line_breakpoint;
+ }
+ else {
+ return master == null? AllIcons.Debugger.Db_disabled_breakpoint : AllIcons.Debugger.Db_dep_line_breakpoint;
+ }
+ }
+
+ protected Icon getSetIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_breakpoint : AllIcons.Debugger.Db_set_breakpoint;
+ }
+
+ protected Icon getInvalidIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_invalid_breakpoint : AllIcons.Debugger.Db_invalid_breakpoint;
+ }
+
+ protected Icon getVerifiedIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_verified_breakpoint : AllIcons.Debugger.Db_verified_breakpoint;
+ }
+
+ protected Icon getVerifiedWarningsIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_verified_warning_breakpoint : AllIcons.Debugger.Db_verified_warning_breakpoint;
+ }
+
+ public Key<LineBreakpoint> getCategory() {
+ return CATEGORY;
+ }
+
+ protected void reload(PsiFile file) {
+ super.reload(file);
+ myMethodName = findMethodName(file, getHighlighter().getStartOffset());
+ }
+
+ protected void createOrWaitPrepare(DebugProcessImpl debugProcess, String classToBeLoaded) {
+ if (isInScopeOf(debugProcess, classToBeLoaded)) {
+ super.createOrWaitPrepare(debugProcess, classToBeLoaded);
+ }
+ }
+
+ protected void createRequestForPreparedClass(final DebugProcessImpl debugProcess, final ReferenceType classType) {
+ if (!isInScopeOf(debugProcess, classType.name())) {
+ return;
+ }
+ try {
+ List<Location> locs = debugProcess.getPositionManager().locationsOfLine(classType, getSourcePosition());
+ if (!locs.isEmpty()) {
+ for (Location loc : locs) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found location [codeIndex=" + loc.codeIndex() +"] for reference type " + classType.name() + " at line " + getLineIndex() + "; isObsolete: " + (debugProcess.getVirtualMachineProxy().versionHigher("1.4") && loc.method().isObsolete()));
+ }
+ BreakpointRequest request = debugProcess.getRequestsManager().createBreakpointRequest(LineBreakpoint.this, loc);
+ debugProcess.getRequestsManager().enableRequest(request);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Created breakpoint request for reference type " + classType.name() + " at line " + getLineIndex() + "; codeIndex=" + loc.codeIndex());
+ }
+ }
+ }
+ else {
+ // there's no executable code in this class
+ debugProcess.getRequestsManager().setInvalid(LineBreakpoint.this, DebuggerBundle.message(
+ "error.invalid.breakpoint.no.executable.code", (getLineIndex() + 1), classType.name())
+ );
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("No locations of type " + classType.name() + " found at line " + getLineIndex());
+ }
+ }
+ }
+ catch (ClassNotPreparedException ex) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ClassNotPreparedException: " + ex.getMessage());
+ }
+ // there's a chance to add a breakpoint when the class is prepared
+ }
+ catch (ObjectCollectedException ex) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ObjectCollectedException: " + ex.getMessage());
+ }
+ // there's a chance to add a breakpoint when the class is prepared
+ }
+ catch (InvalidLineNumberException ex) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("InvalidLineNumberException: " + ex.getMessage());
+ }
+ debugProcess.getRequestsManager().setInvalid(LineBreakpoint.this, DebuggerBundle.message("error.invalid.breakpoint.bad.line.number"));
+ }
+ catch (InternalException ex) {
+ LOG.info(ex);
+ }
+ catch(Exception ex) {
+ LOG.info(ex);
+ }
+ updateUI();
+ }
+
+ private boolean isInScopeOf(DebugProcessImpl debugProcess, String className) {
+ final SourcePosition position = getSourcePosition();
+ if (position != null) {
+ final VirtualFile breakpointFile = position.getFile().getVirtualFile();
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+ if (breakpointFile != null && fileIndex.isInSourceContent(breakpointFile)) {
+ // apply filtering to breakpoints from content sources only, not for sources attached to libraries
+ final Collection<VirtualFile> candidates = findClassCandidatesInSourceContent(className, debugProcess.getSearchScope(), fileIndex);
+ if (candidates == null) {
+ return true;
+ }
+ for (VirtualFile classFile : candidates) {
+ if (breakpointFile.equals(classFile)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Nullable
+ private Collection<VirtualFile> findClassCandidatesInSourceContent(final String className, final GlobalSearchScope scope, final ProjectFileIndex fileIndex) {
+ final int dollarIndex = className.indexOf("$");
+ final String topLevelClassName = dollarIndex >= 0? className.substring(0, dollarIndex) : className;
+ return ApplicationManager.getApplication().runReadAction(new Computable<Collection<VirtualFile>>() {
+ @Nullable
+ public Collection<VirtualFile> compute() {
+ final PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(topLevelClassName, scope);
+ if (classes.length == 0) {
+ return null;
+ }
+ final List<VirtualFile> list = new ArrayList<VirtualFile>(classes.length);
+ for (PsiClass aClass : classes) {
+ final PsiFile psiFile = aClass.getContainingFile();
+ if (psiFile != null) {
+ final VirtualFile vFile = psiFile.getVirtualFile();
+ if (vFile != null && fileIndex.isInSourceContent(vFile)) {
+ list.add(vFile);
+ }
+ }
+ }
+ return list;
+ }
+ });
+ }
+
+ public boolean evaluateCondition(EvaluationContextImpl context, LocatableEvent event) throws EvaluateException {
+ if(CLASS_FILTERS_ENABLED){
+ String className = null;
+ final ObjectReference thisObject = (ObjectReference)context.getThisObject();
+ if(thisObject != null) {
+ className = thisObject.referenceType().name();
+ }
+ else {
+ final StackFrameProxyImpl frame = context.getFrameProxy();
+ if (frame != null) {
+ className = frame.location().declaringType().name();
+ }
+ }
+ if (className != null) {
+ boolean matches = false;
+ for (ClassFilter classFilter : getClassFilters()) {
+ if (classFilter.isEnabled() && classFilter.matches(className)) {
+ matches = true;
+ break;
+ }
+ }
+ if(!matches) {
+ return false;
+ }
+ for (ClassFilter classFilter : getClassExclusionFilters()) {
+ if (classFilter.isEnabled() && classFilter.matches(className)) {
+ return false;
+ }
+ }
+ }
+ }
+ return super.evaluateCondition(context, event);
+ }
+
+ public String toString() {
+ return getDescription();
+ }
+
+ @Override
+ public String getShortName() {
+ return getDisplayInfoInternal(false, 30);
+ }
+
+ public String getDisplayName() {
+ return getDisplayInfoInternal(true, -1);
+ }
+
+ private String getDisplayInfoInternal(boolean showPackageInfo, int totalTextLength) {
+ final RangeHighlighter highlighter = getHighlighter();
+ if(highlighter.isValid() && isValid()) {
+ final int lineNumber = (highlighter.getDocument().getLineNumber(highlighter.getStartOffset()) + 1);
+ String className = getClassName();
+ final boolean hasClassInfo = className != null && className.length() > 0;
+ final boolean hasMethodInfo = myMethodName != null && myMethodName.length() > 0;
+ if (hasClassInfo || hasMethodInfo) {
+ final StringBuilder info = StringBuilderSpinAllocator.alloc();
+ try {
+ boolean isFile = getSourcePosition().getFile().getName().equals(className);
+ String packageName = null;
+ if (hasClassInfo) {
+ final int dotIndex = className.lastIndexOf(".");
+ if (dotIndex >= 0 && !isFile) {
+ packageName = className.substring(0, dotIndex);
+ className = className.substring(dotIndex + 1);
+ }
+
+ if (totalTextLength != -1) {
+ if (className.length() + (hasMethodInfo ? myMethodName.length() : 0) > totalTextLength + 3) {
+ int offset = totalTextLength - (hasMethodInfo ? myMethodName.length() : 0);
+ if (offset > 0 && offset < className.length()) {
+ className = className.substring(className.length() - offset);
+ info.append("...");
+ }
+ }
+ }
+
+ info.append(className);
+ }
+ if(hasMethodInfo) {
+ if (isFile) {
+ info.append(":");
+ }
+ else if (hasClassInfo) {
+ info.append(".");
+ }
+ info.append(myMethodName);
+ }
+ if (showPackageInfo && packageName != null) {
+ info.append(" (").append(packageName).append(")");
+ }
+ return DebuggerBundle.message("line.breakpoint.display.name.with.class.or.method", lineNumber, info.toString());
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(info);
+ }
+ }
+ return DebuggerBundle.message("line.breakpoint.display.name", lineNumber);
+ }
+ return DebuggerBundle.message("status.breakpoint.invalid");
+ }
+
+ private static @Nullable String findMethodName(final PsiFile file, final int offset) {
+ if (file instanceof JspFile) {
+ return null;
+ }
+ if (file instanceof PsiClassOwner) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ public String compute() {
+ final PsiMethod method = DebuggerUtilsEx.findPsiMethod(file, offset);
+ return method != null? method.getName() + "()" : null;
+ }
+ });
+ }
+ return null;
+ }
+
+ public String getEventMessage(LocatableEvent event) {
+ final Location location = event.location();
+ String sourceName = "Unknown Source";
+ try {
+ sourceName = location.sourceName();
+ }
+ catch (AbsentInformationException e) {
+ sourceName = getSourcePosition().getFile().getName();
+ }
+
+ final boolean printFullTrace = Registry.is("debugger.breakpoint.message.full.trace");
+
+ StringBuilder builder = new StringBuilder();
+ if (printFullTrace) {
+ builder.append(DebuggerBundle.message(
+ "status.line.breakpoint.reached.full.trace",
+ location.declaringType().name() + "." + location.method().name())
+ );
+ try {
+ final List<StackFrame> frames = event.thread().frames();
+ renderTrace(frames, builder);
+ }
+ catch (IncompatibleThreadStateException e) {
+ builder.append("Stacktrace not available: ").append(e.getMessage());
+ }
+ }
+ else {
+ builder.append(DebuggerBundle.message(
+ "status.line.breakpoint.reached",
+ location.declaringType().name() + "." + location.method().name(),
+ sourceName,
+ getLineIndex() + 1
+ ));
+ }
+ return builder.toString();
+ }
+
+ private static void renderTrace(List<StackFrame> frames, StringBuilder buffer) {
+ for (final StackFrame stackFrame : frames) {
+ final Location location = stackFrame.location();
+ buffer.append("\n\t ").append(ThreadDumpAction.renderLocation(location));
+ }
+ }
+
+ public PsiElement getEvaluationElement() {
+ return PositionUtil.getContextElement(getSourcePosition());
+ }
+
+ protected static LineBreakpoint create(Project project, Document document, int lineIndex) {
+ VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
+ if (virtualFile == null) {
+ return null;
+ }
+
+ final RangeHighlighter highlighter = createHighlighter(project, document, lineIndex);
+ if (highlighter == null) {
+ return null;
+ }
+
+ LineBreakpoint breakpoint = new LineBreakpoint(project, highlighter);
+ return (LineBreakpoint)breakpoint.init();
+ }
+
+ public boolean canMoveTo(SourcePosition position) {
+ if (!super.canMoveTo(position)) {
+ return false;
+ }
+ final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(position.getFile());
+ return canAddLineBreakpoint(myProject, document, position.getLine());
+ }
+
+ public static boolean canAddLineBreakpoint(Project project, final Document document, final int lineIndex) {
+ if (lineIndex < 0 || lineIndex >= document.getLineCount()) {
+ return false;
+ }
+ final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
+ final LineBreakpoint breakpointAtLine = breakpointManager.findBreakpoint( document, document.getLineStartOffset(lineIndex), CATEGORY);
+ if (breakpointAtLine != null) {
+ // there already exists a line breakpoint at this line
+ return false;
+ }
+ PsiDocumentManager.getInstance(project).commitDocument(document);
+
+ final boolean[] canAdd = new boolean[]{false};
+ XDebuggerUtil.getInstance().iterateLine(project, document, lineIndex, new Processor<PsiElement>() {
+ public boolean process(PsiElement element) {
+ if ((element instanceof PsiWhiteSpace) || (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null)) {
+ return true;
+ }
+ PsiElement child = element;
+ while(element != null) {
+
+ final int offset = element.getTextOffset();
+ if (offset >= 0) {
+ if (document.getLineNumber(offset) != lineIndex) {
+ break;
+ }
+ }
+ child = element;
+ element = element.getParent();
+ }
+
+ if(child instanceof PsiMethod && child.getTextRange().getEndOffset() >= document.getLineEndOffset(lineIndex)) {
+ PsiCodeBlock body = ((PsiMethod)child).getBody();
+ if(body == null) {
+ canAdd[0] = false;
+ }
+ else {
+ PsiStatement[] statements = body.getStatements();
+ canAdd[0] = statements.length > 0 && document.getLineNumber(statements[0].getTextOffset()) == lineIndex;
+ }
+ }
+ else {
+ canAdd[0] = true;
+ }
+ return false;
+ }
+ });
+
+ return canAdd[0];
+ }
+
+ public @Nullable String getMethodName() {
+ return myMethodName;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpointFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpointFactory.java
new file mode 100644
index 0000000..dede7de
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpointFactory.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.ui.breakpoints.actions.*;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Key;
+import org.jdom.Element;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 26, 2005
+ */
+public class LineBreakpointFactory extends BreakpointFactory {
+ public Breakpoint createBreakpoint(Project project, final Element element) {
+ return new LineBreakpoint(project);
+ }
+
+ public Icon getIcon() {
+ return AllIcons.Debugger.Db_set_breakpoint;
+ }
+
+ public Icon getDisabledIcon() {
+ return AllIcons.Debugger.Db_disabled_breakpoint;
+ }
+
+ @Override
+ protected String getHelpID() {
+ return HelpID.LINE_BREAKPOINTS;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return DebuggerBundle.message("line.breakpoints.tab.title");
+ }
+
+ @Override
+ public BreakpointPropertiesPanel createBreakpointPropertiesPanel(Project project, boolean compact) {
+ return new LineBreakpointPropertiesPanel(project, compact);
+ }
+
+ @Override
+ protected BreakpointPanelAction[] createBreakpointPanelActions(Project project, final DialogWrapper parentDialog) {
+ return new BreakpointPanelAction[]{new SwitchViewAction(),
+ new GotoSourceAction(project) {
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ parentDialog.close(DialogWrapper.OK_EXIT_CODE);
+ }
+ },
+ new ViewSourceAction(project),
+ new RemoveAction(project),
+ new ToggleGroupByMethodsAction(),
+ new ToggleGroupByClassesAction(),
+ new ToggleFlattenPackagesAction(),
+ };
+ }
+
+ public Key<LineBreakpoint> getBreakpointCategory() {
+ return LineBreakpoint.CATEGORY;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpointPropertiesPanel.java
new file mode 100644
index 0000000..802657b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpointPropertiesPanel.java
@@ -0,0 +1,29 @@
+/*
+ * 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 LineBreakpointPropertiesPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.openapi.project.Project;
+
+public class LineBreakpointPropertiesPanel extends BreakpointPropertiesPanel {
+ public LineBreakpointPropertiesPanel(Project project, boolean compact) {
+ super(project, LineBreakpoint.CATEGORY, compact);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java
new file mode 100644
index 0000000..4cc4eb0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java
@@ -0,0 +1,383 @@
+/*
+ * 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 MethodBreakpoint
+ * @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.JVMName;
+import com.intellij.debugger.engine.JVMNameUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.project.IndexNotReadyException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.*;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.text.CharArrayUtil;
+import com.sun.jdi.AbsentInformationException;
+import com.sun.jdi.Location;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.event.LocatableEvent;
+import com.sun.jdi.event.MethodEntryEvent;
+import com.sun.jdi.event.MethodExitEvent;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.MethodEntryRequest;
+import com.sun.jdi.request.MethodExitRequest;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Iterator;
+import java.util.Set;
+
+public class MethodBreakpoint extends BreakpointWithHighlighter {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.MethodBreakpoint");
+ public boolean WATCH_ENTRY = true;
+ public boolean WATCH_EXIT = true;
+
+ @Nullable private String myMethodName;
+ @Nullable private JVMName mySignature;
+ private boolean myIsStatic;
+
+ public static final @NonNls Key<MethodBreakpoint> CATEGORY = BreakpointCategory.lookup("method_breakpoints");
+
+ protected MethodBreakpoint(@NotNull Project project) {
+ super(project);
+ }
+
+ private MethodBreakpoint(@NotNull Project project, @NotNull RangeHighlighter highlighter) {
+ super(project, highlighter);
+ }
+
+ public boolean isStatic() {
+ return myIsStatic;
+ }
+
+ @NotNull
+ public Key<MethodBreakpoint> getCategory() {
+ return CATEGORY;
+ }
+
+ @Nullable
+ public PsiMethod getPsiMethod() {
+ Document document = getDocument();
+ if(document == null) return null;
+ PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
+ if(psiFile instanceof PsiJavaFile) {
+ int line = getLineIndex();
+ final int offset = CharArrayUtil.shiftForward(document.getCharsSequence(), document.getLineStartOffset(line), " \t");
+ return DebuggerUtilsEx.findPsiMethod(psiFile, offset);
+ }
+ return null;
+ }
+
+ public boolean isValid() {
+ return super.isValid() && myMethodName != null;
+ }
+
+ protected void reload(@NotNull PsiFile psiFile) {
+ myMethodName = null;
+ mySignature = null;
+
+ MethodDescriptor descriptor = getMethodDescriptor(myProject, psiFile, getSourcePosition());
+ if (descriptor != null) {
+ myMethodName = descriptor.methodName;
+ mySignature = descriptor.methodSignature;
+ myIsStatic = descriptor.isStatic;
+ }
+ if (myIsStatic) {
+ INSTANCE_FILTERS_ENABLED = false;
+ }
+ }
+
+ protected void createRequestForPreparedClass(@NotNull DebugProcessImpl debugProcess, @NotNull ReferenceType classType) {
+ try {
+ boolean hasMethod = false;
+ for (Iterator iterator = classType.allMethods().iterator(); iterator.hasNext();) {
+ Method method = (Method)iterator.next();
+ String signature = method.signature();
+ String name = method.name();
+
+ if (myMethodName.equals(name) && mySignature.getName(debugProcess).equals(signature)) {
+ hasMethod = true;
+ break;
+ }
+ }
+
+ if(!hasMethod) {
+ debugProcess.getRequestsManager().setInvalid(
+ this, DebuggerBundle.message("error.invalid.breakpoint.method.not.found", classType.name())
+ );
+ return;
+ }
+
+ RequestManagerImpl requestManager = debugProcess.getRequestsManager();
+ if (WATCH_ENTRY) {
+ MethodEntryRequest entryRequest = (MethodEntryRequest)findRequest(debugProcess, MethodEntryRequest.class);
+ if (entryRequest == null) {
+ entryRequest = requestManager.createMethodEntryRequest(this);
+ }
+ else {
+ entryRequest.disable();
+ }
+ //entryRequest.addClassFilter(myClassQualifiedName);
+ // use addClassFilter(ReferenceType) in order to stop on subclasses also!
+ entryRequest.addClassFilter(classType);
+ debugProcess.getRequestsManager().enableRequest(entryRequest);
+ }
+ if (WATCH_EXIT) {
+ MethodExitRequest exitRequest = (MethodExitRequest)findRequest(debugProcess, MethodExitRequest.class);
+ if (exitRequest == null) {
+ exitRequest = requestManager.createMethodExitRequest(this);
+ }
+ else {
+ exitRequest.disable();
+ }
+ //exitRequest.addClassFilter(myClassQualifiedName);
+ exitRequest.addClassFilter(classType);
+ debugProcess.getRequestsManager().enableRequest(exitRequest);
+ }
+ }
+ catch (Exception e) {
+ LOG.debug(e);
+ }
+ }
+
+
+ public String getEventMessage(@NotNull 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 = getSourcePosition().getFile().getName();
+ }
+ final int locationLine = location.lineNumber();
+ if (event instanceof MethodEntryEvent) {
+ MethodEntryEvent entryEvent = (MethodEntryEvent)event;
+ final Method method = entryEvent.method();
+ return DebuggerBundle.message(
+ "status.method.entry.breakpoint.reached",
+ method.declaringType().name() + "." + method.name() + "()",
+ locationQName,
+ locationFileName,
+ locationLine
+ );
+ }
+ if (event instanceof MethodExitEvent) {
+ MethodExitEvent exitEvent = (MethodExitEvent)event;
+ final Method method = exitEvent.method();
+ return DebuggerBundle.message(
+ "status.method.exit.breakpoint.reached",
+ method.declaringType().name() + "." + method.name() + "()",
+ locationQName,
+ locationFileName,
+ locationLine
+ );
+ }
+ return "";
+ }
+
+ public PsiElement getEvaluationElement() {
+ return getPsiClass();
+ }
+
+ @NotNull
+ protected Icon getDisabledIcon(boolean isMuted) {
+ final Breakpoint master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this);
+ if (isMuted) {
+ return master == null? AllIcons.Debugger.Db_muted_disabled_method_breakpoint : AllIcons.Debugger.Db_muted_dep_method_breakpoint;
+ }
+ else {
+ return master == null? AllIcons.Debugger.Db_disabled_method_breakpoint : AllIcons.Debugger.Db_dep_method_breakpoint;
+ }
+ }
+
+ @NotNull
+ protected Icon getSetIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_method_breakpoint : AllIcons.Debugger.Db_method_breakpoint;
+ }
+
+ @NotNull
+ protected Icon getInvalidIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_invalid_method_breakpoint : AllIcons.Debugger.Db_invalid_method_breakpoint;
+ }
+
+ @NotNull
+ protected Icon getVerifiedIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_verified_method_breakpoint : AllIcons.Debugger.Db_verified_method_breakpoint;
+ }
+
+ @NotNull
+ protected Icon getVerifiedWarningsIcon(boolean isMuted) {
+ return isMuted? AllIcons.Debugger.Db_muted_method_warning_breakpoint : AllIcons.Debugger.Db_method_warning_breakpoint;
+ }
+
+ public String getDisplayName() {
+ final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ if(isValid()) {
+ final String className = getClassName();
+ final boolean classNameExists = className != null && className.length() > 0;
+ if (classNameExists) {
+ buffer.append(className);
+ }
+ if(myMethodName != null) {
+ if (classNameExists) {
+ buffer.append(".");
+ }
+ buffer.append(myMethodName);
+ }
+ }
+ else {
+ buffer.append(DebuggerBundle.message("status.breakpoint.invalid"));
+ }
+ return buffer.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ public boolean evaluateCondition(@NotNull EvaluationContextImpl context, @NotNull LocatableEvent event) throws EvaluateException {
+ if (!matchesEvent(event, context.getDebugProcess())) {
+ return false;
+ }
+ return super.evaluateCondition(context, event);
+ }
+
+ public boolean matchesEvent(@NotNull final LocatableEvent event, final DebugProcessImpl process) throws EvaluateException {
+ if (myMethodName == null || mySignature == null) {
+ return false;
+ }
+ final Method method = event.location().method();
+ return method != null && method.name().equals(myMethodName) && method.signature().equals(mySignature.getName(process));
+ }
+
+ @Nullable
+ public static MethodBreakpoint create(@NotNull Project project, @NotNull Document document, int lineIndex) {
+ final MethodBreakpoint breakpoint = new MethodBreakpoint(project, createHighlighter(project, document, lineIndex));
+ return (MethodBreakpoint)breakpoint.init();
+ }
+
+
+ public boolean canMoveTo(final SourcePosition position) {
+ return super.canMoveTo(position) && PositionUtil.getPsiElementAt(getProject(), PsiMethod.class, position) != null;
+ }
+
+ /**
+ * finds FQ method's class name and method's signature
+ */
+ @Nullable
+ private static MethodDescriptor getMethodDescriptor(@NotNull final Project project, @NotNull final PsiFile psiJavaFile, @NotNull final SourcePosition sourcePosition) {
+ final PsiDocumentManager docManager = PsiDocumentManager.getInstance(project);
+ final Document document = docManager.getDocument(psiJavaFile);
+ if(document == null) {
+ return null;
+ }
+ //final int endOffset = document.getLineEndOffset(sourcePosition);
+ final MethodDescriptor descriptor = docManager.commitAndRunReadAction(new Computable<MethodDescriptor>() {
+ @Nullable
+ public MethodDescriptor compute() {
+ //PsiMethod method = DebuggerUtilsEx.findPsiMethod(psiJavaFile, endOffset);
+ PsiMethod method = PositionUtil.getPsiElementAt(project, PsiMethod.class, sourcePosition);
+ if (method == null) {
+ return null;
+ }
+ final int methodOffset = method.getTextOffset();
+ if (methodOffset < 0) {
+ return null;
+ }
+ if (document.getLineNumber(methodOffset) < sourcePosition.getLine()) {
+ return null;
+ }
+
+ final PsiIdentifier identifier = method.getNameIdentifier();
+ int methodNameOffset = identifier != null? identifier.getTextOffset() : methodOffset;
+ final MethodDescriptor descriptor =
+ new MethodDescriptor();
+ //noinspection HardCodedStringLiteral
+ descriptor.methodName = method.isConstructor() ? "<init>" : method.getName();
+ try {
+ descriptor.methodSignature = JVMNameUtil.getJVMSignature(method);
+ descriptor.isStatic = method.hasModifierProperty(PsiModifier.STATIC);
+ }
+ catch (IndexNotReadyException ignored) {
+ return null;
+ }
+ descriptor.methodLine = document.getLineNumber(methodNameOffset);
+ return descriptor;
+ }
+ });
+ if (descriptor == null || descriptor.methodName == null || descriptor.methodSignature == null) {
+ return null;
+ }
+ return descriptor;
+ }
+
+ @Nullable
+ private EventRequest findRequest(@NotNull DebugProcessImpl debugProcess, Class requestClass) {
+ Set reqSet = debugProcess.getRequestsManager().findRequests(this);
+ for (Iterator iterator = reqSet.iterator(); iterator.hasNext();) {
+ EventRequest eventRequest = (EventRequest) iterator.next();
+ if(eventRequest.getClass().equals(requestClass)) {
+ return eventRequest;
+ }
+ }
+
+ return null;
+ }
+
+ public String toString() {
+ return getDescription();
+ }
+
+ public boolean isBodyAt(@NotNull Document document, int offset) {
+ PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
+ if(psiFile instanceof PsiJavaFile) {
+ PsiMethod method = DebuggerUtilsEx.findPsiMethod(psiFile, offset);
+ return method == getPsiMethod();
+ }
+
+ return false;
+ }
+
+ private static final class MethodDescriptor {
+ String methodName;
+ JVMName methodSignature;
+ boolean isStatic;
+ int methodLine;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpointFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpointFactory.java
new file mode 100644
index 0000000..c2ad3dc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpointFactory.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.HelpID;
+import com.intellij.debugger.ui.breakpoints.actions.*;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Key;
+import org.jdom.Element;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Apr 26, 2005
+ */
+public class MethodBreakpointFactory extends BreakpointFactory{
+ public Breakpoint createBreakpoint(Project project, final Element element) {
+ return element.getAttributeValue(WildcardMethodBreakpoint.JDOM_LABEL) != null? new WildcardMethodBreakpoint(project) : new MethodBreakpoint(project);
+ }
+
+ public Icon getIcon() {
+ return AllIcons.Debugger.Db_method_breakpoint;
+ }
+
+ public Icon getDisabledIcon() {
+ return AllIcons.Debugger.Db_disabled_method_breakpoint;
+ }
+
+ @Override
+ protected String getHelpID() {
+ return HelpID.METHOD_BREAKPOINTS;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return DebuggerBundle.message("method.breakpoints.tab.title");
+ }
+
+ @Override
+ public BreakpointPropertiesPanel createBreakpointPropertiesPanel(Project project, boolean compact) {
+ return new MethodBreakpointPropertiesPanel(project, compact);
+ }
+
+ @Override
+ protected BreakpointPanelAction[] createBreakpointPanelActions(Project project, final DialogWrapper parentDialog) {
+ return new BreakpointPanelAction[]{
+ new SwitchViewAction(),
+ new AddAction(this, project),
+ new GotoSourceAction(project) {
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ parentDialog.close(DialogWrapper.OK_EXIT_CODE);
+ }
+ },
+ new ViewSourceAction(project),
+ new RemoveAction(project),
+ new ToggleGroupByClassesAction(),
+ new ToggleFlattenPackagesAction(),
+ };
+ }
+
+ @Override
+ protected void configureBreakpointPanel(BreakpointPanel panel) {
+ super.configureBreakpointPanel(panel);
+ panel.getTree().setGroupByMethods(false);
+ }
+
+ public Key<MethodBreakpoint> getBreakpointCategory() {
+ return MethodBreakpoint.CATEGORY;
+ }
+
+ @Override
+ public boolean canAddBreakpoints() {
+ return true;
+ }
+
+ public WildcardMethodBreakpoint addBreakpoint(Project project) {
+ AddWildcardBreakpointDialog dialog = new AddWildcardBreakpointDialog(project);
+ dialog.show();
+ WildcardMethodBreakpoint methodBreakpoint;
+ methodBreakpoint = !dialog.isOK()
+ ? null
+ : DebuggerManagerEx.getInstanceEx(project).getBreakpointManager()
+ .addMethodBreakpoint(dialog.getClassPattern(), dialog.getMethodName());
+ return methodBreakpoint;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpointPropertiesPanel.java
new file mode 100644
index 0000000..5298474
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpointPropertiesPanel.java
@@ -0,0 +1,117 @@
+/*
+ * 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 MethodBreakpointPropertiesPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.ui.DialogUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class MethodBreakpointPropertiesPanel extends BreakpointPropertiesPanel {
+ private JCheckBox myWatchEntryCheckBox;
+ private JCheckBox myWatchExitCheckBox;
+
+ public MethodBreakpointPropertiesPanel(final Project project, boolean compact) {
+ super(project, MethodBreakpoint.CATEGORY, compact);
+ }
+
+ protected JComponent createSpecialBox() {
+ JPanel _panel, _panel0;
+
+ myWatchEntryCheckBox = new JCheckBox(DebuggerBundle.message("label.method.breakpoint.properties.panel.method.entry"));
+ myWatchExitCheckBox = new JCheckBox(DebuggerBundle.message("label.method.breakpoint.properties.panel.method.exit"));
+ DialogUtil.registerMnemonic(myWatchEntryCheckBox);
+ DialogUtil.registerMnemonic(myWatchExitCheckBox);
+
+
+ Box watchBox = Box.createVerticalBox();
+ _panel = new JPanel(new BorderLayout());
+ _panel.add(myWatchEntryCheckBox, BorderLayout.NORTH);
+ watchBox.add(_panel);
+ _panel = new JPanel(new BorderLayout());
+ _panel.add(myWatchExitCheckBox, BorderLayout.NORTH);
+ watchBox.add(_panel);
+
+ _panel = new JPanel(new BorderLayout());
+ _panel0 = new JPanel(new BorderLayout());
+ _panel0.add(watchBox, BorderLayout.CENTER);
+ _panel0.add(Box.createHorizontalStrut(3), BorderLayout.WEST);
+ _panel0.add(Box.createHorizontalStrut(3), BorderLayout.EAST);
+ _panel.add(_panel0, BorderLayout.NORTH);
+ _panel.setBorder(IdeBorderFactory.createTitledBorder(DebuggerBundle.message("label.group.watch.events"), true));
+
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JCheckBox toCheck = null;
+ if (!myWatchEntryCheckBox.isSelected() && !myWatchExitCheckBox.isSelected()) {
+ Object source = e.getSource();
+ if (myWatchEntryCheckBox.equals(source)) {
+ toCheck = myWatchExitCheckBox;
+ }
+ else if (myWatchExitCheckBox.equals(source)) {
+ toCheck = myWatchEntryCheckBox;
+ }
+ if (toCheck != null) {
+ toCheck.setSelected(true);
+ }
+ }
+ }
+ };
+ myWatchEntryCheckBox.addActionListener(listener);
+ myWatchExitCheckBox.addActionListener(listener);
+
+ return _panel;
+ }
+
+ public void initFrom(Breakpoint breakpoint, boolean moreOptionsVisible) {
+ super.initFrom(breakpoint, moreOptionsVisible);
+ if (breakpoint instanceof MethodBreakpoint) {
+ MethodBreakpoint methodBreakpoint = (MethodBreakpoint)breakpoint;
+ myWatchEntryCheckBox.setSelected(methodBreakpoint.WATCH_ENTRY);
+ myWatchExitCheckBox.setSelected(methodBreakpoint.WATCH_EXIT);
+ }
+ else if (breakpoint instanceof WildcardMethodBreakpoint){
+ final WildcardMethodBreakpoint methodBreakpoint = ((WildcardMethodBreakpoint)breakpoint);
+ myWatchEntryCheckBox.setSelected(methodBreakpoint.WATCH_ENTRY);
+ myWatchExitCheckBox.setSelected(methodBreakpoint.WATCH_EXIT);
+ }
+ }
+
+ public void saveTo(Breakpoint breakpoint, @NotNull Runnable afterUpdate) {
+ if (breakpoint instanceof MethodBreakpoint) {
+ MethodBreakpoint methodBreakpoint = (MethodBreakpoint)breakpoint;
+ methodBreakpoint.WATCH_ENTRY = myWatchEntryCheckBox.isSelected();
+ methodBreakpoint.WATCH_EXIT = myWatchExitCheckBox.isSelected();
+ }
+ else if (breakpoint instanceof WildcardMethodBreakpoint){
+ final WildcardMethodBreakpoint methodBreakpoint = ((WildcardMethodBreakpoint)breakpoint);
+ methodBreakpoint.WATCH_ENTRY = myWatchEntryCheckBox.isSelected();
+ methodBreakpoint.WATCH_EXIT = myWatchExitCheckBox.isSelected();
+ }
+ super.saveTo(breakpoint, afterUpdate);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java
new file mode 100644
index 0000000..7c7c5eb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Sep 13, 2006
+ */
+public class RunToCursorBreakpoint extends LineBreakpoint {
+ private final boolean myRestoreBreakpoints;
+
+ private RunToCursorBreakpoint(Project project, RangeHighlighter highlighter, boolean restoreBreakpoints) {
+ super(project, highlighter);
+ setVisible(false);
+ myRestoreBreakpoints = restoreBreakpoints;
+ }
+
+ public boolean isRestoreBreakpoints() {
+ return myRestoreBreakpoints;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return false;
+ }
+
+ @Override
+ protected boolean isMuted(@NotNull final DebugProcessImpl debugProcess) {
+ return false; // always enabled
+ }
+
+ @Nullable
+ protected static RunToCursorBreakpoint create(@NotNull Project project, @NotNull Document document, int lineIndex, boolean restoreBreakpoints) {
+ VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
+ if (virtualFile == null) {
+ return null;
+ }
+
+ final RangeHighlighter highlighter = createHighlighter(project, document, lineIndex);
+ if (highlighter == null) {
+ return null;
+ }
+
+ final RunToCursorBreakpoint breakpoint = new RunToCursorBreakpoint(project, highlighter, restoreBreakpoints);
+ breakpoint.getHighlighter().dispose();
+
+ return (RunToCursorBreakpoint)breakpoint.init();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/WildcardMethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/WildcardMethodBreakpoint.java
new file mode 100644
index 0000000..f6c0dc3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/WildcardMethodBreakpoint.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2004-2006 Alexey Efimov
+ *
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.requests.RequestManagerImpl;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.AbsentInformationException;
+import com.sun.jdi.Location;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.event.LocatableEvent;
+import com.sun.jdi.event.MethodEntryEvent;
+import com.sun.jdi.event.MethodExitEvent;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.MethodEntryRequest;
+import com.sun.jdi.request.MethodExitRequest;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Iterator;
+import java.util.Set;
+
+public class WildcardMethodBreakpoint extends Breakpoint {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.ExceptionBreakpoint");
+
+ public boolean WATCH_ENTRY = true;
+ public boolean WATCH_EXIT = true;
+ private String myClassPattern;
+ private String myMethodName;
+
+ public static final String JDOM_LABEL = "wildcard_breakpoint";
+
+ public WildcardMethodBreakpoint(Project project) {
+ super(project);
+ }
+
+ public Key<MethodBreakpoint> getCategory() {
+ return MethodBreakpoint.CATEGORY;
+ }
+
+ protected WildcardMethodBreakpoint(Project project, @NotNull String classPattern, @NotNull String methodName) {
+ super(project);
+ myClassPattern = classPattern;
+ myMethodName = methodName;
+ }
+
+ public String getClassName() {
+ return myClassPattern;
+ }
+
+ public @Nullable String getShortClassName() {
+ return getClassName();
+ }
+
+ public String getMethodName() {
+ return myMethodName;
+ }
+
+ public PsiClass getPsiClass() {
+ return null;
+ }
+
+ public String getDisplayName() {
+ if (!isValid()) {
+ return DebuggerBundle.message("status.breakpoint.invalid");
+ }
+ final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ buffer.append(myClassPattern);
+ buffer.append(".");
+ buffer.append(myMethodName);
+ buffer.append("()");
+ return buffer.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ public Icon getIcon() {
+ if (!ENABLED) {
+ final Breakpoint master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this);
+ return master == null? AllIcons.Debugger.Db_disabled_method_breakpoint : AllIcons.Debugger.Db_dep_method_breakpoint;
+ }
+ return AllIcons.Debugger.Db_method_breakpoint;
+ }
+
+ public void reload() {
+ }
+
+ public boolean evaluateCondition(EvaluationContextImpl context, LocatableEvent event) throws EvaluateException {
+ return matchesEvent(event) && super.evaluateCondition(context, event);
+ }
+
+ public void createRequest(DebugProcessImpl debugProcess) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (!ENABLED || !debugProcess.isAttached() || debugProcess.areBreakpointsMuted() || !debugProcess.getRequestsManager().findRequests(this).isEmpty()) {
+ return;
+ }
+ try {
+ RequestManagerImpl requestManager = debugProcess.getRequestsManager();
+ if (WATCH_ENTRY) {
+ MethodEntryRequest entryRequest = (MethodEntryRequest)findRequest(debugProcess, MethodEntryRequest.class);
+ if (entryRequest == null) {
+ entryRequest = requestManager.createMethodEntryRequest(this);
+ }
+ else {
+ entryRequest.disable();
+ }
+ entryRequest.addClassFilter(myClassPattern);
+ debugProcess.getRequestsManager().enableRequest(entryRequest);
+ }
+ if (WATCH_EXIT) {
+ MethodExitRequest exitRequest = (MethodExitRequest)findRequest(debugProcess, MethodExitRequest.class);
+ if (exitRequest == null) {
+ exitRequest = requestManager.createMethodExitRequest(this);
+ }
+ else {
+ exitRequest.disable();
+ }
+ exitRequest.addClassFilter(myClassPattern);
+ debugProcess.getRequestsManager().enableRequest(exitRequest);
+ }
+ }
+ catch (Exception e) {
+ LOG.debug(e);
+ }
+ }
+
+ private EventRequest findRequest(DebugProcessImpl debugProcess, Class requestClass) {
+ Set reqSet = debugProcess.getRequestsManager().findRequests(this);
+ for (Iterator iterator = reqSet.iterator(); iterator.hasNext();) {
+ EventRequest eventRequest = (EventRequest) iterator.next();
+ if(eventRequest.getClass().equals(requestClass)) {
+ return eventRequest;
+ }
+ }
+
+ return null;
+ }
+
+ public void processClassPrepare(DebugProcess debugProcess, ReferenceType refType) {
+ // should be emty - does not make sense for this breakpoint
+ }
+
+ public String getEventMessage(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 = "";
+ }
+ final int locationLine = location.lineNumber();
+
+ if (event instanceof MethodEntryEvent) {
+ MethodEntryEvent entryEvent = (MethodEntryEvent)event;
+ final Method method = entryEvent.method();
+ return DebuggerBundle.message(
+ "status.method.entry.breakpoint.reached",
+ method.declaringType().name() + "." + method.name() + "()",
+ locationQName,
+ locationFileName,
+ locationLine
+ );
+ }
+
+ if (event instanceof MethodExitEvent) {
+ MethodExitEvent exitEvent = (MethodExitEvent)event;
+ final Method method = exitEvent.method();
+ return DebuggerBundle.message(
+ "status.method.exit.breakpoint.reached",
+ method.declaringType().name() + "." + method.name() + "()",
+ locationQName,
+ locationFileName,
+ locationLine
+ );
+ }
+ return "";
+ }
+
+ public boolean isValid() {
+ return myClassPattern != null && myMethodName != null;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) public void writeExternal(Element parentNode) throws WriteExternalException {
+ super.writeExternal(parentNode);
+ parentNode.setAttribute(JDOM_LABEL, "true");
+ if (myClassPattern != null) {
+ parentNode.setAttribute("class_name", myClassPattern);
+ }
+ if (myMethodName != null) {
+ parentNode.setAttribute("method_name", myMethodName);
+ }
+ }
+
+ public PsiElement getEvaluationElement() {
+ return null;
+ }
+
+ public void readExternal(Element parentNode) throws InvalidDataException {
+ super.readExternal(parentNode);
+
+ //noinspection HardCodedStringLiteral
+ String className = parentNode.getAttributeValue("class_name");
+ myClassPattern = className;
+
+ //noinspection HardCodedStringLiteral
+ String methodName = parentNode.getAttributeValue("method_name");
+ myMethodName = methodName;
+
+ if(className == null || methodName == null) {
+ throw new InvalidDataException();
+ }
+ }
+
+ public boolean matchesEvent(final LocatableEvent event){
+ final Method method = event.location().method();
+ return method != null && myMethodName.equals(method.name());
+ }
+
+ public static WildcardMethodBreakpoint create(Project project, final String classPattern, final String methodName) {
+ return new WildcardMethodBreakpoint(project, classPattern, methodName);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/AddAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/AddAction.java
new file mode 100644
index 0000000..8e3b37e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/AddAction.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointFactory;
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.project.Project;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class AddAction extends BreakpointPanelAction {
+ protected final Project myProject;
+ protected BreakpointFactory myBreakpointFactory;
+
+ public AddAction(BreakpointFactory breakpointFactory, Project project) {
+ super(IdeBundle.message("button.add"));
+ myProject = project;
+ this.myBreakpointFactory = breakpointFactory;
+ }
+
+ public void setPanel(BreakpointPanel panel) {
+ super.setPanel(panel);
+ getPanel().getTable().registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ }
+
+ public void update() {
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Breakpoint breakpoint = myBreakpointFactory.addBreakpoint(myProject);
+ if (breakpoint != null) {
+ getPanel().addBreakpoint(breakpoint);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/BreakpointPanelAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/BreakpointPanelAction.java
new file mode 100644
index 0000000..32e7f5c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/BreakpointPanelAction.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+
+import javax.swing.*;
+import java.awt.event.ActionListener;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public abstract class BreakpointPanelAction implements ActionListener {
+ private final String myName;
+ private AbstractButton myButton;
+ private BreakpointPanel myPanel;
+
+ protected BreakpointPanelAction(String name) {
+ myName = name;
+ }
+
+ public final String getName() {
+ return myName;
+ }
+
+ public boolean isStateAction() {
+ return false;
+ }
+
+ public void setPanel(BreakpointPanel panel) {
+ myPanel = panel;
+ }
+
+ public final BreakpointPanel getPanel() {
+ return myPanel;
+ }
+
+ public void setButton(AbstractButton button) {
+ myButton = button;
+ }
+
+ public final AbstractButton getButton() {
+ return myButton;
+ }
+
+ public abstract void update();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/FocusOnBreakpointAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/FocusOnBreakpointAction.java
new file mode 100644
index 0000000..91b223c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/FocusOnBreakpointAction.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.execution.ui.actions.AbstractFocusOnAction;
+import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
+
+public class FocusOnBreakpointAction extends AbstractFocusOnAction {
+ public FocusOnBreakpointAction() {
+ super(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/GotoSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/GotoSourceAction.java
new file mode 100644
index 0000000..aedbfb4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/GotoSourceAction.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class GotoSourceAction extends BreakpointPanelAction {
+ private final Project myProject;
+
+ protected GotoSourceAction(final Project project) {
+ super(IdeBundle.message("button.go.to"));
+ myProject = project;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ gotoSource();
+ }
+
+ private void gotoSource() {
+ OpenFileDescriptor editSourceDescriptor = getPanel().createEditSourceDescriptor(myProject);
+ if (editSourceDescriptor != null) {
+ FileEditorManager.getInstance(myProject).openTextEditor(editSourceDescriptor, true);
+ }
+ }
+
+ public void setPanel(BreakpointPanel panel) {
+ super.setPanel(panel);
+ ShortcutSet shortcutSet = ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getShortcutSet();
+ new AnAction() {
+ public void actionPerformed(AnActionEvent e){
+ gotoSource();
+ }
+ }.registerCustomShortcutSet(shortcutSet, getPanel().getPanel());
+ }
+
+ public void update() {
+ getButton().setEnabled(getPanel().getCurrentViewableBreakpoint() != null);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/RemoveAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/RemoveAction.java
new file mode 100644
index 0000000..72a86b7
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/RemoveAction.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.ui.breakpoints.AnyExceptionBreakpoint;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+import com.intellij.openapi.project.Project;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class RemoveAction extends BreakpointPanelAction {
+ private final Project myProject;
+
+ public RemoveAction(final Project project) {
+ super(DebuggerBundle.message("button.remove"));
+ myProject = project;
+ }
+
+
+ public void setPanel(BreakpointPanel panel) {
+ super.setPanel(panel);
+ getPanel().getTable().registerKeyboardAction(
+ this, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
+ );
+ getPanel().getTree().registerKeyboardAction(
+ this, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
+ );
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Breakpoint[] breakpoints = getPanel().getSelectedBreakpoints();
+ if (breakpoints != null) {
+ for (Breakpoint breakpoint1 : breakpoints) {
+ if (breakpoint1 instanceof AnyExceptionBreakpoint) {
+ return;
+ }
+ }
+ getPanel().removeSelectedBreakpoints();
+ BreakpointManager manager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+ for (final Breakpoint breakpoint : breakpoints) {
+ getPanel().getTree().removeBreakpoint(breakpoint);
+ manager.removeBreakpoint(breakpoint);
+ }
+ }
+ }
+
+ public void update() {
+ getButton().setEnabled(getPanel().getSelectedBreakpoints().length > 0);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/SwitchViewAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/SwitchViewAction.java
new file mode 100644
index 0000000..8a44fbb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/SwitchViewAction.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class SwitchViewAction extends BreakpointPanelAction {
+ public SwitchViewAction() {
+ super(DebuggerBundle.message("button.switch.view"));
+ }
+
+
+ public void actionPerformed(ActionEvent e) {
+ getPanel().switchViews();
+ }
+
+
+ public void update() {
+ final AbstractButton button = getButton();
+ final BreakpointPanel panel = getPanel();
+ button.setText(panel.isTreeShowing()? DebuggerBundle.message("button.list.view") : DebuggerBundle.message("button.tree.view"));
+ button.setEnabled(panel.getBreakpointCount() > 0);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleFlattenPackagesAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleFlattenPackagesAction.java
new file mode 100644
index 0000000..d6681f4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleFlattenPackagesAction.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+import com.intellij.debugger.DebuggerBundle;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class ToggleFlattenPackagesAction extends BreakpointPanelAction {
+ public ToggleFlattenPackagesAction() {
+ super(DebuggerBundle.message("button.flatten.packages"));
+ }
+
+ public boolean isStateAction() {
+ return true;
+ }
+
+ public void setButton(AbstractButton button) {
+ super.setButton(button);
+ getButton().setSelected(getPanel().getTree().isFlattenPackages());
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ getPanel().getTree().setFlattenPackages(getButton().isSelected());
+ }
+
+ public void update() {
+ final AbstractButton button = getButton();
+ final BreakpointPanel panel = getPanel();
+ button.setEnabled(panel.getBreakpointCount() > 0 && panel.isTreeShowing());
+ button.setSelected(panel.getTree().isFlattenPackages());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleGroupByClassesAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleGroupByClassesAction.java
new file mode 100644
index 0000000..7dec6f3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleGroupByClassesAction.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+import com.intellij.debugger.DebuggerBundle;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class ToggleGroupByClassesAction extends BreakpointPanelAction {
+ public ToggleGroupByClassesAction() {
+ super(DebuggerBundle.message("button.group.by.classes"));
+ }
+
+ public boolean isStateAction() {
+ return true;
+ }
+
+ public void setButton(AbstractButton button) {
+ super.setButton(button);
+ getButton().setSelected(getPanel().getTree().isGroupByClasses());
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ getPanel().getTree().setGroupByClasses(getButton().isSelected());
+ }
+
+ public void update() {
+ final AbstractButton button = getButton();
+ final BreakpointPanel panel = getPanel();
+ button.setEnabled(panel.getBreakpointCount() > 0 && panel.isTreeShowing());
+ button.setSelected(panel.getTree().isGroupByClasses());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleGroupByMethodsAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleGroupByMethodsAction.java
new file mode 100644
index 0000000..57fcabe
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ToggleGroupByMethodsAction.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.debugger.ui.breakpoints.BreakpointPanel;
+import com.intellij.debugger.DebuggerBundle;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class ToggleGroupByMethodsAction extends BreakpointPanelAction {
+ public ToggleGroupByMethodsAction() {
+ super(DebuggerBundle.message("button.group.by.methods"));
+ }
+
+ public boolean isStateAction() {
+ return true;
+ }
+
+ public void setButton(AbstractButton button) {
+ super.setButton(button);
+ getButton().setSelected(getPanel().getTree().isGroupByMethods());
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ getPanel().getTree().setGroupByMethods(getButton().isSelected());
+ }
+
+ public void update() {
+ final AbstractButton button = getButton();
+ final BreakpointPanel panel = getPanel();
+ button.setEnabled(panel.getBreakpointCount() > 0 && panel.isTreeShowing());
+ button.setSelected(panel.getTree().isGroupByMethods());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ViewSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ViewSourceAction.java
new file mode 100644
index 0000000..0744ef9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/actions/ViewSourceAction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.breakpoints.actions;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 25, 2005
+ */
+public class ViewSourceAction extends BreakpointPanelAction {
+ private final Project myProject;
+
+ public ViewSourceAction(final Project project) {
+ super(IdeBundle.message("button.view.source"));
+ myProject = project;
+ }
+
+
+ public void actionPerformed(ActionEvent e) {
+ OpenFileDescriptor editSourceDescriptor = getPanel().createEditSourceDescriptor(myProject);
+ if (editSourceDescriptor != null) {
+ FileEditorManager.getInstance(myProject).openTextEditor(editSourceDescriptor, false);
+ }
+ }
+
+ public void update() {
+ getButton().setEnabled(getPanel().getCurrentViewableBreakpoint() != null);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/content/DebuggerContentUIFacade.java b/java/debugger/impl/src/com/intellij/debugger/ui/content/DebuggerContentUIFacade.java
new file mode 100644
index 0000000..9d0bcbc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/content/DebuggerContentUIFacade.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.content;
+
+import com.intellij.ui.content.ContentUI;
+
+public interface DebuggerContentUIFacade {
+
+ ContentUI getContentUI();
+
+ void restoreLayout();
+
+ boolean isHorizontalToolbar();
+
+ void setHorizontalToolbar(final boolean state);
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerComboBoxRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerComboBoxRenderer.java
new file mode 100644
index 0000000..015884f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerComboBoxRenderer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
+import com.intellij.ui.ListCellRendererWrapper;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class DebuggerComboBoxRenderer extends ListCellRendererWrapper {
+ public DebuggerComboBoxRenderer(final ListCellRenderer listCellRenderer) {
+ super();
+ }
+
+ @Override
+ public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (list.getComponentCount() > 0) {
+ Icon icon = getIcon(value);
+ if (icon != null) {
+ setIcon(icon);
+ }
+ }
+ else {
+ setIcon(null);
+ }
+ }
+
+ @Nullable
+ private static Icon getIcon(Object item) {
+ if (item instanceof ThreadDescriptorImpl) {
+ return ((ThreadDescriptorImpl)item).getIcon();
+ }
+ if (item instanceof StackFrameDescriptorImpl) {
+ return ((StackFrameDescriptorImpl)item).getIcon();
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeBase.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeBase.java
new file mode 100644
index 0000000..f515f94
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeBase.java
@@ -0,0 +1,347 @@
+/*
+ * 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 DebuggerTreeBase
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.ide.dnd.aware.DnDAwareTree;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.ui.ScreenUtil;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.util.text.StringTokenizer;
+import com.intellij.util.ui.GeometryUtil;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+
+public class DebuggerTreeBase extends DnDAwareTree implements Disposable {
+ private final Project myProject;
+ private DebuggerTreeNodeImpl myCurrentTooltipNode;
+
+ private JComponent myCurrentTooltip;
+
+ protected final TipManager myTipManager;
+
+ public DebuggerTreeBase(TreeModel model, Project project) {
+ super(model);
+ myProject = project;
+
+ myTipManager = new TipManager(this, new TipManager.TipFactory() {
+ @Override
+ public JComponent createToolTip(MouseEvent e) {
+ return DebuggerTreeBase.this.createToolTip(e);
+ }
+
+ @Override
+ public MouseEvent createTooltipEvent(MouseEvent candidateEvent) {
+ return DebuggerTreeBase.this.createTooltipEvent(candidateEvent);
+ }
+
+ @Override
+ public boolean isFocusOwner() {
+ return DebuggerTreeBase.this.isFocusOwner();
+ }
+ });
+
+ Disposer.register(this, myTipManager);
+
+ UIUtil.setLineStyleAngled(this);
+ setRootVisible(false);
+ setShowsRootHandles(true);
+ setCellRenderer(new DebuggerTreeRenderer());
+ updateUI();
+ TreeUtil.installActions(this);
+ }
+
+ private JComponent createTipContent(String tipText, DebuggerTreeNodeImpl node) {
+ final JToolTip tooltip = new JToolTip();
+
+ if (tipText == null) {
+ tooltip.setTipText(tipText);
+ }
+ else {
+ Dimension rootSize = getVisibleRect().getSize();
+ Insets borderInsets = tooltip.getBorder().getBorderInsets(tooltip);
+ rootSize.width -= (borderInsets.left + borderInsets.right) * 2;
+ rootSize.height -= (borderInsets.top + borderInsets.bottom) * 2;
+
+ @NonNls StringBuilder tipBuilder = new StringBuilder();
+ final String markupText = node.getMarkupTooltipText();
+ if (markupText != null) {
+ tipBuilder.append(markupText);
+ }
+
+ if (!tipText.isEmpty()) {
+ final StringTokenizer tokenizer = new StringTokenizer(tipText, "\n ", true);
+
+ while (tokenizer.hasMoreElements()) {
+ final String each = tokenizer.nextElement();
+ if ("\n".equals(each)) {
+ tipBuilder.append("<br>");
+ }
+ else if (" ".equals(each)) {
+ tipBuilder.append("  ");
+ }
+ else {
+ tipBuilder.append(JDOMUtil.legalizeText(each));
+ }
+ }
+ }
+
+ tooltip.setTipText(UIUtil.toHtml(tipBuilder.toString(), 0));
+ }
+
+ tooltip.setBorder(null);
+
+ return tooltip;
+ }
+
+ public MouseEvent createTooltipEvent(MouseEvent candidate) {
+ TreePath path = null;
+
+ if (candidate != null) {
+ final Point treePoint = SwingUtilities.convertPoint(candidate.getComponent(), candidate.getPoint(), this);
+ if (GeometryUtil.isWithin(new Rectangle(0, 0, getWidth(), getHeight()), treePoint)) {
+ path = getPathForLocation(treePoint.x, treePoint.y);
+ }
+ }
+
+ if (path == null) {
+ if (isFocusOwner()) {
+ path = getSelectionPath();
+ }
+ }
+
+ if (path == null) return null;
+
+ final int row = getRowForPath(path);
+ if (row == -1) return null;
+
+ final Rectangle bounds = getRowBounds(row);
+
+ return new MouseEvent(this, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, bounds.x,
+ bounds.y + bounds.height - bounds.height / 4, 0, false);
+ }
+
+ @Nullable
+ public JComponent createToolTip(MouseEvent e) {
+ final DebuggerTreeNodeImpl node = getNodeToShowTip(e);
+ if (node == null) {
+ return null;
+ }
+
+ if (myCurrentTooltip != null && myCurrentTooltip.isShowing() && myCurrentTooltipNode == node) {
+ return myCurrentTooltip;
+ }
+
+ myCurrentTooltipNode = node;
+
+ final String toolTipText = getTipText(node);
+ if (toolTipText == null) {
+ return null;
+ }
+
+ final JComponent tipContent = createTipContent(toolTipText, node);
+ final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(tipContent);
+ scrollPane.setBorder(null);
+ scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
+
+ final Point point = e.getPoint();
+ SwingUtilities.convertPointToScreen(point, e.getComponent());
+ Rectangle tipRectangle = new Rectangle(point, tipContent.getPreferredSize());
+
+ final Rectangle screen = ScreenUtil.getScreenRectangle(point.x, point.y);
+
+ final JToolTip toolTip = new JToolTip();
+
+ tipContent.addMouseListener(new HideTooltip(toolTip));
+
+ final Border tooltipBorder = toolTip.getBorder();
+ if (tooltipBorder != null) {
+ final Insets borderInsets = tooltipBorder.getBorderInsets(this);
+ tipRectangle
+ .setSize(tipRectangle.width + borderInsets.left + borderInsets.right, tipRectangle.height + borderInsets.top + borderInsets.bottom);
+ }
+
+ toolTip.setLayout(new BorderLayout());
+ toolTip.add(scrollPane, BorderLayout.CENTER);
+
+
+ tipRectangle.height += scrollPane.getHorizontalScrollBar().getPreferredSize().height;
+ tipRectangle.width += scrollPane.getVerticalScrollBar().getPreferredSize().width;
+
+
+ final int maxWidth = (int)(screen.width - screen.width * .25);
+ if (tipRectangle.width > maxWidth) {
+ tipRectangle.width = maxWidth;
+ }
+
+ final Dimension prefSize = tipRectangle.getSize();
+
+ ScreenUtil.cropRectangleToFitTheScreen(tipRectangle);
+
+ if (prefSize.width > tipRectangle.width) {
+ final int delta = prefSize.width - tipRectangle.width;
+ tipRectangle.x -= delta;
+ if (tipRectangle.x < screen.x) {
+ tipRectangle.x = screen.x + maxWidth / 2;
+ tipRectangle.width = screen.width - maxWidth / 2;
+ }
+ else {
+ tipRectangle.width += delta;
+ }
+ }
+
+ toolTip.setPreferredSize(tipRectangle.getSize());
+
+ myCurrentTooltip = toolTip;
+
+ return myCurrentTooltip;
+ }
+
+ @Nullable
+ private String getTipText(DebuggerTreeNodeImpl node) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (descriptor instanceof ValueDescriptorImpl) {
+ String text = ((ValueDescriptorImpl)descriptor).getValueLabel();
+ if (text != null) {
+ if (StringUtil.startsWithChar(text, '{') && text.indexOf('}') > 0) {
+ int idx = text.indexOf('}');
+ if (idx != text.length() - 1) {
+ text = text.substring(idx + 1);
+ }
+ }
+
+ if (StringUtil.startsWithChar(text, '\"') && StringUtil.endsWithChar(text, '\"')) {
+ text = text.substring(1, text.length() - 1);
+ }
+
+ final String tipText = prepareToolTipText(text);
+ if (!tipText.isEmpty() &&
+ (tipText.indexOf('\n') >= 0 || !getVisibleRect().contains(getRowBounds(getRowForPath(new TreePath(node.getPath())))))) {
+ return tipText;
+ }
+ }
+ }
+ return node.getMarkupTooltipText() != null? "" : null;
+ }
+
+ @Nullable
+ private DebuggerTreeNodeImpl getNodeToShowTip(MouseEvent event) {
+ TreePath path = getPathForLocation(event.getX(), event.getY());
+ if (path != null) {
+ Object last = path.getLastPathComponent();
+ if (last instanceof DebuggerTreeNodeImpl) {
+ return (DebuggerTreeNodeImpl)last;
+ }
+ }
+
+ return null;
+ }
+
+ private String prepareToolTipText(String text) {
+ int tabSize = CodeStyleSettingsManager.getSettings(myProject).getTabSize(StdFileTypes.JAVA);
+ if (tabSize < 0) {
+ tabSize = 0;
+ }
+ final StringBuilder buf = new StringBuilder();
+ boolean special = false;
+ for (int idx = 0; idx < text.length(); idx++) {
+ char c = text.charAt(idx);
+ if (special) {
+ if (c == 't') { // convert tabs to spaces
+ for (int i = 0; i < tabSize; i++) {
+ buf.append(' ');
+ }
+ }
+ else if (c == 'r') { // remove occurrences of '\r'
+ }
+ else if (c == 'n') {
+ buf.append('\n');
+ }
+ else {
+ buf.append('\\');
+ buf.append(c);
+ }
+ special = false;
+ }
+ else {
+ if (c == '\\') {
+ special = true;
+ }
+ else {
+ buf.append(c);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public void dispose() {
+ final JComponent tooltip = myCurrentTooltip;
+ if (tooltip != null) {
+ tooltip.setVisible(false);
+ }
+ myCurrentTooltip = null;
+ myCurrentTooltipNode = null;
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ private static class HideTooltip extends MouseAdapter {
+ private final JToolTip myToolTip;
+
+ public HideTooltip(JToolTip toolTip) {
+ myToolTip = toolTip;
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ if (UIUtil.isActionClick(e)) {
+ final Window wnd = SwingUtilities.getWindowAncestor(myToolTip);
+ if (wnd instanceof JWindow) {
+ wnd.setVisible(false);
+ }
+ }
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreePanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreePanel.java
new file mode 100644
index 0000000..9caf776
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreePanel.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
+import com.intellij.ui.PopupHandler;
+import com.intellij.util.Alarm;
+import com.intellij.xdebugger.impl.actions.XDebuggerActions;
+import com.sun.jdi.VMDisconnectedException;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+
+public abstract class DebuggerTreePanel extends UpdatableDebuggerView implements DataProvider {
+ public static final DataKey<DebuggerTreePanel> DATA_KEY = DataKey.create("DebuggerPanel");
+
+ private final Alarm myRebuildAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+ protected DebuggerTree myTree;
+
+ public DebuggerTreePanel(Project project, DebuggerStateManager stateManager) {
+ super(project, stateManager);
+ myTree = createTreeView();
+
+ final PopupHandler popupHandler = new PopupHandler() {
+ public void invokePopup(Component comp, int x, int y) {
+ ActionPopupMenu popupMenu = createPopupMenu();
+ if (popupMenu != null) {
+ myTree.myTipManager.registerPopup(popupMenu.getComponent()).show(comp, x, y);
+ }
+ }
+ };
+ myTree.addMouseListener(popupHandler);
+
+ setFocusTraversalPolicy(new IdeFocusTraversalPolicy() {
+ public Component getDefaultComponentImpl(Container focusCycleRoot) {
+ return myTree;
+ }
+ });
+
+ registerDisposable(new Disposable() {
+ public void dispose() {
+ myTree.removeMouseListener(popupHandler);
+ }
+ });
+
+ final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("ToggleBookmark");
+ final CustomShortcutSet shortcutSet = shortcuts.length > 0? new CustomShortcutSet(shortcuts) : new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0));
+ overrideShortcut(myTree, XDebuggerActions.MARK_OBJECT, shortcutSet);
+ }
+
+ protected abstract DebuggerTree createTreeView();
+
+
+ protected void rebuild(int event) {
+ myRebuildAlarm.cancelAllRequests();
+ myRebuildAlarm.addRequest(new Runnable() {
+ public void run() {
+ try {
+ final DebuggerContextImpl context = getContext();
+ if(context.getDebuggerSession() != null) {
+ getTree().rebuild(context);
+ }
+ }
+ catch (VMDisconnectedException e) {
+ // ignored
+ }
+ }
+ }, 100, ModalityState.NON_MODAL);
+ }
+
+ public void dispose() {
+ Disposer.dispose(myRebuildAlarm);
+ try {
+ super.dispose();
+ }
+ finally {
+ final DebuggerTree tree = myTree;
+ if (tree != null) {
+ Disposer.dispose(tree);
+ }
+ // prevent mem leak from inside Swing
+ myTree = null;
+ }
+ }
+
+
+ protected abstract ActionPopupMenu createPopupMenu();
+
+ public final DebuggerTree getTree() {
+ return myTree;
+ }
+
+ public void clear() {
+ myTree.removeAllChildren();
+ }
+
+ public Object getData(String dataId) {
+ if (DebuggerTreePanel.DATA_KEY.is(dataId)) {
+ return this;
+ }
+ return null;
+ }
+
+ public void requestFocus() {
+ getTree().requestFocus();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java
new file mode 100644
index 0000000..e515575
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.editor.SyntaxHighlighterColors;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.*;
+import com.intellij.util.PlatformIcons;
+import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DebuggerTreeRenderer extends ColoredTreeCellRenderer {
+
+ private static final SimpleTextAttributes DEFAULT_ATTRIBUTES = new SimpleTextAttributes(Font.PLAIN, null);
+ private static final SimpleTextAttributes SPECIAL_NODE_ATTRIBUTES = new SimpleTextAttributes(Font.PLAIN, new JBColor(Color.lightGray, Gray._130));
+ private static final SimpleTextAttributes OBJECT_ID_HIGHLIGHT_ATTRIBUTES = new SimpleTextAttributes(Font.PLAIN, new JBColor(Color.lightGray, Gray._130));
+
+ public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ final DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl) value;
+
+ if (node != null) {
+ final SimpleColoredText text = node.getText();
+ if (text != null) {
+ text.appendToComponent(this);
+ }
+ setIcon(node.getIcon());
+ }
+ }
+
+ @Nullable
+ public static Icon getDescriptorIcon(NodeDescriptorImpl descriptor) {
+ Icon nodeIcon = null;
+ if (descriptor instanceof ThreadGroupDescriptorImpl) {
+ nodeIcon = (((ThreadGroupDescriptorImpl)descriptor).isCurrent() ? AllIcons.Debugger.ThreadGroupCurrent : AllIcons.Debugger.ThreadGroup);
+ }
+ else if (descriptor instanceof ThreadDescriptorImpl) {
+ ThreadDescriptorImpl threadDescriptor = (ThreadDescriptorImpl)descriptor;
+ nodeIcon = threadDescriptor.getIcon();
+ }
+ else if (descriptor instanceof StackFrameDescriptorImpl) {
+ StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)descriptor;
+ nodeIcon = stackDescriptor.getIcon();
+ }
+ else if (descriptor instanceof ValueDescriptorImpl) {
+ final ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
+ if (valueDescriptor instanceof FieldDescriptorImpl && ((FieldDescriptorImpl)valueDescriptor).isStatic()) {
+ nodeIcon = PlatformIcons.FIELD_ICON;
+ }
+ else if (valueDescriptor.isArray()) {
+ nodeIcon = AllIcons.Debugger.Db_array;
+ }
+ else if (valueDescriptor.isPrimitive()) {
+ nodeIcon = AllIcons.Debugger.Db_primitive;
+ }
+ else {
+ if (valueDescriptor instanceof WatchItemDescriptor) {
+ nodeIcon = AllIcons.Debugger.Watch;
+ }
+ else {
+ nodeIcon = AllIcons.Debugger.Value;
+ }
+ }
+ final Icon valueIcon = valueDescriptor.getValueIcon();
+ if (nodeIcon != null && valueIcon != null) {
+ final RowIcon composite = new RowIcon(2);
+ composite.setIcon(nodeIcon, 0);
+ composite.setIcon(valueIcon, 1);
+ nodeIcon = composite;
+ }
+ }
+ else if (descriptor instanceof MessageDescriptor) {
+ MessageDescriptor messageDescriptor = (MessageDescriptor)descriptor;
+ if (messageDescriptor.getKind() == MessageDescriptor.ERROR) {
+ nodeIcon = XDebuggerUIConstants.ERROR_MESSAGE_ICON;
+ }
+ else if (messageDescriptor.getKind() == MessageDescriptor.INFORMATION) {
+ nodeIcon = XDebuggerUIConstants.INFORMATION_MESSAGE_ICON;
+ }
+ else if (messageDescriptor.getKind() == MessageDescriptor.SPECIAL) {
+ nodeIcon = null;
+ }
+ }
+ else if (descriptor instanceof StaticDescriptorImpl) {
+ nodeIcon = AllIcons.Nodes.Static;
+ }
+
+ return nodeIcon;
+ }
+
+ public static SimpleColoredText getDescriptorText(final DebuggerContextImpl debuggerContext, NodeDescriptorImpl descriptor, boolean multiline) {
+ return getDescriptorText(debuggerContext, descriptor, multiline, true);
+ }
+
+ public static SimpleColoredText getDescriptorTitle(final DebuggerContextImpl debuggerContext, NodeDescriptorImpl descriptor) {
+ return getDescriptorText(debuggerContext, descriptor, false, false);
+ }
+
+ private static SimpleColoredText getDescriptorText(final DebuggerContextImpl debuggerContext, final NodeDescriptorImpl descriptor, boolean multiline,
+ boolean appendValue) {
+ SimpleColoredText descriptorText = new SimpleColoredText();
+
+ String text;
+ String nodeName;
+
+ if (descriptor == null) {
+ text = "";
+ nodeName = null;
+ }
+ else {
+ text = descriptor.getLabel();
+ nodeName = descriptor.getName();
+ }
+
+ if(text.equals(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
+ descriptorText.append(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE, XDebuggerUIConstants.COLLECTING_DATA_HIGHLIGHT_ATTRIBUTES);
+ return descriptorText;
+ }
+
+ if (descriptor instanceof ValueDescriptor) {
+ final ValueMarkup markup = ((ValueDescriptor)descriptor).getMarkup(debuggerContext.getDebugProcess());
+ if (markup != null) {
+ descriptorText.append("[" + markup.getText() + "] ", new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, markup.getColor()));
+ }
+ }
+
+ String[] strings = breakString(text, nodeName);
+
+ if (strings[0] != null) {
+ if (descriptor instanceof MessageDescriptor && ((MessageDescriptor)descriptor).getKind() == MessageDescriptor.SPECIAL) {
+ descriptorText.append(strings[0], SPECIAL_NODE_ATTRIBUTES);
+ }
+ else {
+ descriptorText.append(strings[0], DEFAULT_ATTRIBUTES);
+ }
+ }
+ if (strings[1] != null) {
+ descriptorText.append(strings[1], XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES);
+ }
+ if (strings[2] != null) {
+ if (descriptor instanceof ValueDescriptorImpl) {
+ if(multiline && strings[2].indexOf('\n') >=0) {
+ strings = breakString(strings[2], "=");
+ if(strings[2] != null) {
+ strings[2] = strings[0] + strings[1] + "\n" + strings[2];
+ }
+ }
+
+
+ ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
+ String valueLabel = valueDescriptor.getValueLabel();
+
+ strings = breakString(strings[2], valueLabel);
+ if (strings[0] != null) {
+ descriptorText.append(strings[0], DEFAULT_ATTRIBUTES);
+ }
+ if (appendValue && strings[1] != null) {
+ if(valueLabel != null && StringUtil.startsWithChar(valueLabel, '{') && valueLabel.indexOf('}') > 0 && !StringUtil.endsWithChar(valueLabel, '}')) {
+ int idx = valueLabel.indexOf('}');
+ String objectId = valueLabel.substring(0, idx + 1);
+ valueLabel = valueLabel.substring(idx + 1);
+ descriptorText.append(objectId, OBJECT_ID_HIGHLIGHT_ATTRIBUTES);
+ }
+
+ valueLabel = DebuggerUtilsEx.truncateString(valueLabel);
+
+ final SimpleTextAttributes valueLabelAttribs;
+ if (valueDescriptor.isDirty()) {
+ valueLabelAttribs = XDebuggerUIConstants.CHANGED_VALUE_ATTRIBUTES;
+ }
+ else {
+ TextAttributes highlightingAttribs = null;
+ if (valueDescriptor.isNull()){
+ highlightingAttribs = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(SyntaxHighlighterColors.KEYWORD);
+ }
+ else if (valueDescriptor.isString()) {
+ highlightingAttribs = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(SyntaxHighlighterColors.STRING);
+ }
+ valueLabelAttribs = highlightingAttribs != null? SimpleTextAttributes.fromTextAttributes(highlightingAttribs) : DEFAULT_ATTRIBUTES;
+ }
+
+ final EvaluateException exception = descriptor.getEvaluateException();
+ if(exception != null) {
+ final String errorMessage = exception.getMessage();
+ if(valueLabel.endsWith(errorMessage)) {
+ appendValueTextWithEscapesRendering(descriptorText, valueLabel.substring(0, valueLabel.length() - errorMessage.length()), valueLabelAttribs);
+ descriptorText.append(errorMessage, XDebuggerUIConstants.EXCEPTION_ATTRIBUTES);
+ }
+ else {
+ appendValueTextWithEscapesRendering(descriptorText, valueLabel, valueLabelAttribs);
+ descriptorText.append(errorMessage, XDebuggerUIConstants.EXCEPTION_ATTRIBUTES);
+ }
+ }
+ else {
+ if(valueLabel.equals(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
+ descriptorText.append(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE, XDebuggerUIConstants.COLLECTING_DATA_HIGHLIGHT_ATTRIBUTES);
+ }
+ else {
+ appendValueTextWithEscapesRendering(descriptorText, valueLabel, valueLabelAttribs);
+ }
+ }
+ }
+ }
+ else {
+ descriptorText.append(strings[2], DEFAULT_ATTRIBUTES);
+ }
+ }
+
+ return descriptorText;
+ }
+
+ private static void appendValueTextWithEscapesRendering(SimpleColoredText descriptorText, String valueText, final SimpleTextAttributes attribs) {
+ SimpleTextAttributes escapeAttribs = null;
+ final StringBuilder buf = new StringBuilder();
+ boolean slashFound = false;
+ for (int idx= 0; idx < valueText.length(); idx++) {
+ final char ch = valueText.charAt(idx);
+ if (slashFound) {
+ slashFound = false;
+ if (ch == '\\' || ch == '\"' || ch == 'b'|| ch == 't'|| ch == 'n'|| ch == 'f'|| ch == 'r' ) {
+ if (buf.length() > 0) {
+ descriptorText.append(buf.toString(), attribs);
+ buf.setLength(0);
+ }
+
+ if (escapeAttribs == null) { // lazy init
+ final TextAttributes fromHighlighter = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(SyntaxHighlighterColors.VALID_STRING_ESCAPE);
+ if (fromHighlighter != null) {
+ escapeAttribs = SimpleTextAttributes.fromTextAttributes(fromHighlighter);
+ }
+ else {
+ escapeAttribs = DEFAULT_ATTRIBUTES.derive(SimpleTextAttributes.STYLE_BOLD, JBColor.GRAY, null, null);
+ }
+ }
+
+ if (ch != '\\' && ch != '\"') {
+ descriptorText.append("\\", escapeAttribs);
+ }
+ descriptorText.append(String.valueOf(ch), escapeAttribs);
+ }
+ else {
+ buf.append('\\').append(ch);
+ }
+ }
+ else {
+ if (ch == '\\') {
+ slashFound = true;
+ }
+ else {
+ buf.append(ch);
+ }
+ }
+ }
+ if (buf.length() > 0) {
+ descriptorText.append(buf.toString(), attribs);
+ }
+ }
+
+ private static String[] breakString(String source, String substr) {
+ if (substr != null && substr.length() > 0) {
+ int index = Math.max(source.indexOf(substr), 0);
+ String prefix = (index > 0) ? source.substring(0, index) : null;
+ index += substr.length();
+ String suffix = (index < source.length() - 1) ? source.substring(index) : null;
+ return new String[]{prefix, substr, suffix};
+ }
+ return new String[]{source, null, null};
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java
new file mode 100644
index 0000000..ba9a9ff
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java
@@ -0,0 +1,569 @@
+/*
+ * 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 FrameDebuggerTree
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.SuspendManager;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.settings.ViewsGeneralSettings;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.lang.java.JavaLanguage;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.text.CharArrayUtil;
+import com.intellij.util.ui.tree.TreeModelAdapter;
+import com.intellij.xdebugger.XDebuggerBundle;
+import com.sun.jdi.AbsentInformationException;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.Value;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import java.util.*;
+
+public class FrameVariablesTree extends DebuggerTree {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.FrameDebuggerTree");
+ private boolean myAnyNewLocals;
+ private boolean myAutoWatchMode = false;
+
+ public FrameVariablesTree(Project project) {
+ super(project);
+ getEmptyText().setText(XDebuggerBundle.message("debugger.variables.not.available"));
+ }
+
+ public boolean isAutoWatchMode() {
+ return myAutoWatchMode;
+ }
+
+ public void setAutoVariablesMode(final boolean autoWatchMode) {
+ final boolean valueChanged = myAutoWatchMode != autoWatchMode;
+ myAutoWatchMode = autoWatchMode;
+ if (valueChanged) {
+ rebuild(getDebuggerContext());
+ }
+ }
+
+ protected void build(DebuggerContextImpl context) {
+ myAnyNewLocals = false;
+ buildWhenPaused(context, new RefreshFrameTreeCommand(context));
+ }
+
+ public void restoreNodeState(DebuggerTreeNodeImpl node) {
+ if (myAnyNewLocals) {
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ final boolean isLocalVar = descriptor instanceof LocalVariableDescriptorImpl;
+ descriptor.myIsSelected &= isLocalVar;
+ // override this setting so that tree will scroll to new locals
+ descriptor.myIsVisible = isLocalVar && descriptor.myIsSelected;
+ if (!descriptor.myIsVisible) {
+ descriptor.putUserData(VISIBLE_RECT, null);
+ }
+ }
+ super.restoreNodeState(node);
+ if (myAnyNewLocals && node.getDescriptor().myIsExpanded) {
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getMutableModel().getRoot();
+ scrollToVisible(root);
+ }
+ }
+
+
+ protected DebuggerCommandImpl getBuildNodeCommand(final DebuggerTreeNodeImpl node) {
+ if (node.getDescriptor() instanceof StackFrameDescriptorImpl) {
+ return new BuildFrameTreeVariablesCommand(node);
+ }
+ return super.getBuildNodeCommand(node);
+ }
+
+ private class BuildFrameTreeVariablesCommand extends BuildStackFrameCommand {
+ public BuildFrameTreeVariablesCommand(DebuggerTreeNodeImpl stackNode) {
+ super(stackNode);
+ }
+
+ protected void buildVariables(final StackFrameDescriptorImpl stackDescriptor, final EvaluationContextImpl evaluationContext) throws EvaluateException {
+ final DebuggerContextImpl debuggerContext = getDebuggerContext();
+ final SourcePosition sourcePosition = debuggerContext.getSourcePosition();
+ if (sourcePosition == null) {
+ return;
+ }
+ try {
+ if (!ViewsGeneralSettings.getInstance().ENABLE_AUTO_EXPRESSIONS && !myAutoWatchMode) {
+ // optimization
+ super.buildVariables(stackDescriptor, evaluationContext);
+ }
+ else {
+ final Map<String, LocalVariableProxyImpl> visibleVariables = getVisibleVariables(stackDescriptor);
+ final EvaluationContextImpl evalContext = debuggerContext.createEvaluationContext();
+ final Pair<Set<String>, Set<TextWithImports>> usedVars =
+ ApplicationManager.getApplication().runReadAction(new Computable<Pair<Set<String>, Set<TextWithImports>>>() {
+ public Pair<Set<String>, Set<TextWithImports>> compute() {
+ return findReferencedVars(visibleVariables.keySet(), sourcePosition, evalContext);
+ }
+ });
+ // add locals
+ if (myAutoWatchMode) {
+ for (String var : usedVars.first) {
+ final LocalVariableDescriptorImpl descriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, visibleVariables.get(var));
+ myChildren.add(myNodeManager.createNode(descriptor, evaluationContext));
+ }
+ }
+ else {
+ super.buildVariables(stackDescriptor, evaluationContext);
+ }
+ // add expressions
+ final EvaluationContextImpl evalContextCopy = evaluationContext.createEvaluationContext(evaluationContext.getThisObject());
+ evalContextCopy.setAutoLoadClasses(false);
+ for (TextWithImports text : usedVars.second) {
+ myChildren.add(myNodeManager.createNode(myNodeManager.getWatchItemDescriptor(stackDescriptor, text, null), evalContextCopy));
+ }
+ }
+ }
+ catch (EvaluateException e) {
+ if (e.getCause() instanceof AbsentInformationException) {
+ final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
+ if (frame == null) {
+ throw e;
+ }
+ final Collection<Value> argValues = frame.getArgumentValues();
+ int index = 0;
+ for (Value argValue : argValues) {
+ final ArgumentValueDescriptorImpl descriptor = myNodeManager.getArgumentValueDescriptor(stackDescriptor, index++, argValue);
+ final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
+ myChildren.add(variableNode);
+ }
+ myChildren.add(myNodeManager.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE));
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ private static Map<String, LocalVariableProxyImpl> getVisibleVariables(final StackFrameDescriptorImpl stackDescriptor) throws EvaluateException {
+ final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
+ if (frame == null) {
+ return Collections.emptyMap();
+ }
+ final Map<String, LocalVariableProxyImpl> vars = new HashMap<String, LocalVariableProxyImpl>();
+ for (LocalVariableProxyImpl localVariableProxy : frame.visibleVariables()) {
+ vars.put(localVariableProxy.name(), localVariableProxy);
+ }
+ return vars;
+ }
+
+ private static boolean shouldSkipLine(final PsiFile file, Document doc, int line) {
+ final int start = doc.getLineStartOffset(line);
+ final int end = doc.getLineEndOffset(line);
+ final int _start = CharArrayUtil.shiftForward(doc.getCharsSequence(), start, " \n\t");
+ if (_start >= end) {
+ return true;
+ }
+
+ TextRange alreadyChecked = null;
+ for (PsiElement elem = file.findElementAt(_start); elem != null && elem.getTextOffset() <= end && (alreadyChecked == null || !alreadyChecked .contains(elem.getTextRange())); elem = elem.getNextSibling()) {
+ for (PsiElement _elem = elem; _elem.getTextOffset() >= _start; _elem = _elem.getParent()) {
+ alreadyChecked = _elem.getTextRange();
+
+ if (_elem instanceof PsiDeclarationStatement) {
+ final PsiElement[] declared = ((PsiDeclarationStatement)_elem).getDeclaredElements();
+ for (PsiElement declaredElement : declared) {
+ if (declaredElement instanceof PsiVariable) {
+ return false;
+ }
+ }
+ }
+
+ if (_elem instanceof PsiJavaCodeReferenceElement) {
+ final PsiElement resolved = ((PsiJavaCodeReferenceElement)_elem).resolve();
+ if (resolved instanceof PsiVariable) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private static Pair<Set<String>, Set<TextWithImports>> findReferencedVars(final Set<String> visibleVars,
+ final SourcePosition position,
+ EvaluationContextImpl evalContext) {
+ final int line = position.getLine();
+ if (line < 0) {
+ return new Pair<Set<String>, Set<TextWithImports>>(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
+ }
+ final PsiFile positionFile = position.getFile();
+ if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
+ return new Pair<Set<String>, Set<TextWithImports>>(visibleVars, Collections.<TextWithImports>emptySet());
+ }
+
+ final VirtualFile vFile = positionFile.getVirtualFile();
+ final Document doc = vFile != null? FileDocumentManager.getInstance().getDocument(vFile) : null;
+ if (doc == null || doc.getLineCount() == 0 || line > (doc.getLineCount() - 1)) {
+ return new Pair<Set<String>, Set<TextWithImports>>(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
+ }
+
+ final TextRange limit = calculateLimitRange(positionFile, doc, line);
+
+ int startLine = Math.max(limit.getStartOffset(), line - 1);
+ startLine = Math.min(startLine, limit.getEndOffset());
+ while (startLine > limit.getStartOffset() && shouldSkipLine(positionFile, doc, startLine)) {
+ startLine--;
+ }
+ final int startOffset = doc.getLineStartOffset(startLine);
+
+ int endLine = Math.min(line + 2, limit.getEndOffset());
+ while (endLine < limit.getEndOffset() && shouldSkipLine(positionFile, doc, endLine)) {
+ endLine++;
+ }
+ final int endOffset = doc.getLineEndOffset(endLine);
+
+ final TextRange lineRange = new TextRange(startOffset, endOffset);
+ if (!lineRange.isEmpty()) {
+ final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), doc.getLineStartOffset(line), " \t");
+ PsiElement element = positionFile.findElementAt(offset);
+ if (element != null) {
+ PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
+ if (method != null) {
+ element = method;
+ }
+ else {
+ PsiField field = PsiTreeUtil.getNonStrictParentOfType(element, PsiField.class);
+ if (field != null) {
+ element = field;
+ }
+ else {
+ final PsiClassInitializer initializer = PsiTreeUtil.getNonStrictParentOfType(element, PsiClassInitializer.class);
+ if (initializer != null) {
+ element = initializer;
+ }
+ }
+ }
+
+ //noinspection unchecked
+ if (element instanceof PsiCompiledElement) {
+ return new Pair<Set<String>, Set<TextWithImports>>(visibleVars, Collections.<TextWithImports>emptySet());
+ }
+ else {
+ final Set<String> vars = new HashSet<String>();
+ final Set<TextWithImports> expressions = new HashSet<TextWithImports>();
+ final PsiElementVisitor variablesCollector = new VariablesCollector(visibleVars, adjustRange(element, lineRange), expressions, vars, position, evalContext);
+ element.accept(variablesCollector);
+
+ return new Pair<Set<String>, Set<TextWithImports>>(vars, expressions);
+ }
+ }
+ }
+ return new Pair<Set<String>, Set<TextWithImports>>(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
+ }
+
+ private static TextRange calculateLimitRange(final PsiFile file, final Document doc, final int line) {
+ final int offset = doc.getLineStartOffset(line);
+ if (offset > 0) {
+ for (PsiElement elem = file.findElementAt(offset); elem != null; elem = elem.getParent()) {
+ if (elem instanceof PsiMethod) {
+ final TextRange elemRange = elem.getTextRange();
+ return new TextRange(doc.getLineNumber(elemRange.getStartOffset()), doc.getLineNumber(elemRange.getEndOffset()));
+ }
+ }
+ }
+ return new TextRange(0, doc.getLineCount() - 1);
+ }
+
+ private static TextRange adjustRange(final PsiElement element, final TextRange originalRange) {
+ final Ref<TextRange> rangeRef = new Ref<TextRange>(originalRange);
+ element.accept(new JavaRecursiveElementVisitor() {
+ @Override public void visitExpressionStatement(final PsiExpressionStatement statement) {
+ final TextRange stRange = statement.getTextRange();
+ if (originalRange.intersects(stRange)) {
+ final TextRange currentRange = rangeRef.get();
+ final int start = Math.min(currentRange.getStartOffset(), stRange.getStartOffset());
+ final int end = Math.max(currentRange.getEndOffset(), stRange.getEndOffset());
+ rangeRef.set(new TextRange(start, end));
+ }
+ }
+ });
+ return rangeRef.get();
+ }
+
+ private class RefreshFrameTreeCommand extends RefreshDebuggerTreeCommand {
+ public RefreshFrameTreeCommand(DebuggerContextImpl context) {
+ super(context);
+ }
+
+ public void contextAction() throws Exception {
+ DebuggerTreeNodeImpl rootNode;
+
+ final DebuggerContextImpl debuggerContext = getDebuggerContext();
+ final ThreadReferenceProxyImpl currentThread = debuggerContext.getThreadProxy();
+ if (currentThread == null) {
+ return;
+ }
+
+ try {
+ StackFrameProxyImpl frame = debuggerContext.getFrameProxy();
+
+ if (frame != null) {
+ NodeManagerImpl nodeManager = getNodeFactory();
+ rootNode = nodeManager.createNode(nodeManager.getStackFrameDescriptor(null, frame), debuggerContext.createEvaluationContext());
+ }
+ else {
+ rootNode = getNodeFactory().getDefaultNode();
+ SuspendManager suspendManager = getSuspendContext().getDebugProcess().getSuspendManager();
+ try {
+ if (suspendManager.isSuspended(currentThread)) {
+ try {
+ if (currentThread.frameCount() == 0) {
+ rootNode.add(MessageDescriptor.THREAD_IS_EMPTY);
+ }
+ else {
+ rootNode.add(MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
+ }
+ }
+ catch (EvaluateException e) {
+ rootNode.add(new MessageDescriptor(e.getMessage()));
+ }
+ }
+ else {
+ rootNode.add(MessageDescriptor.THREAD_IS_RUNNING);
+ }
+ }
+ catch (ObjectCollectedException e) {
+ rootNode.add(new MessageDescriptor(DebuggerBundle.message("label.thread.node.thread.collected", currentThread.name())));
+ }
+ }
+ }
+ catch (Exception ex) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(ex);
+ }
+ rootNode = getNodeFactory().getDefaultNode();
+ rootNode.add(MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
+ }
+
+ final DebuggerTreeNodeImpl rootNode1 = rootNode;
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ getMutableModel().setRoot(rootNode1);
+ treeChanged();
+
+ final TreeModel model = getModel();
+ model.addTreeModelListener(new TreeModelAdapter() {
+ public void treeStructureChanged(TreeModelEvent e) {
+ final Object[] path = e.getPath();
+ if (path.length > 0 && path[path.length - 1] == rootNode1) {
+ // wait until rootNode1 (the root just set) becomes the root
+ model.removeTreeModelListener(this);
+ if (ViewsGeneralSettings.getInstance().AUTOSCROLL_TO_NEW_LOCALS) {
+ autoscrollToNewLocals(rootNode1);
+ }
+ else {
+ // should clear this flag, otherwise, if AUTOSCROLL_TO_NEW_LOCALS option turned
+ // to true during the debug process, all these variables will be considered 'new'
+ for (Enumeration children = rootNode1.rawChildren(); children.hasMoreElements();) {
+ final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)children.nextElement();
+ final NodeDescriptorImpl descriptor = child.getDescriptor();
+ if (descriptor instanceof LocalVariableDescriptorImpl) {
+ ((LocalVariableDescriptorImpl)descriptor).setNewLocal(false);
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ private void autoscrollToNewLocals(DebuggerTreeNodeImpl frameNode) {
+ final DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
+ final boolean isSteppingThrough = debuggerSession.isSteppingThrough(debuggerContext.getThreadProxy());
+ final List<DebuggerTreeNodeImpl> toClear = new ArrayList<DebuggerTreeNodeImpl>();
+ final List<DebuggerTreeNodeImpl> newLocalsToSelect = new ArrayList<DebuggerTreeNodeImpl>();
+
+ for (Enumeration e = frameNode.rawChildren(); e.hasMoreElements();) {
+ final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
+ final NodeDescriptorImpl descriptor = child.getDescriptor();
+ if (!(descriptor instanceof LocalVariableDescriptorImpl)) {
+ continue;
+ }
+ final LocalVariableDescriptorImpl localVariableDescriptor = (LocalVariableDescriptorImpl)descriptor;
+ if (isSteppingThrough && localVariableDescriptor.isNewLocal()) {
+ myAnyNewLocals = true;
+ newLocalsToSelect.add(child);
+ }
+ else {
+ toClear.add(child);
+ }
+ localVariableDescriptor.setNewLocal(false);
+ }
+
+ if (!newLocalsToSelect.isEmpty()) {
+ for (DebuggerTreeNodeImpl child : toClear) {
+ removeSelectionPath(new TreePath(child.getPath()));
+ child.getDescriptor().myIsSelected = false;
+ }
+ for (DebuggerTreeNodeImpl child : newLocalsToSelect) {
+ addSelectionPath(new TreePath(child.getPath()));
+ child.getDescriptor().myIsSelected = true;
+ }
+ }
+ }
+ });
+ }
+
+ }
+
+ private static class VariablesCollector extends JavaRecursiveElementVisitor {
+ private final Set<String> myVisibleLocals;
+ private final TextRange myLineRange;
+ private final Set<TextWithImports> myExpressions;
+ private final Set<String> myVars;
+ private final SourcePosition myPosition;
+ private final EvaluationContextImpl myEvalContext;
+ private final boolean myCollectExpressions;
+
+ public VariablesCollector(final Set<String> visibleLocals,
+ final TextRange lineRange,
+ final Set<TextWithImports> expressions,
+ final Set<String> vars,
+ SourcePosition position, EvaluationContextImpl evalContext) {
+ myVisibleLocals = visibleLocals;
+ myLineRange = lineRange;
+ myExpressions = expressions;
+ myVars = vars;
+ myPosition = position;
+ myEvalContext = evalContext;
+ myCollectExpressions = ViewsGeneralSettings.getInstance().ENABLE_AUTO_EXPRESSIONS;
+ }
+
+ @Override
+ public void visitElement(final PsiElement element) {
+ if (myLineRange.intersects(element.getTextRange())) {
+ super.visitElement(element);
+ }
+ }
+
+ @Override
+ public void visitMethodCallExpression(final PsiMethodCallExpression expression) {
+ if (myCollectExpressions) {
+ final PsiMethod psiMethod = expression.resolveMethod();
+ if (psiMethod != null && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
+ myExpressions.add(new TextWithImportsImpl(expression));
+ }
+ }
+ super.visitMethodCallExpression(expression);
+ }
+
+ @Override
+ public void visitReferenceExpression(final PsiReferenceExpression reference) {
+ if (myLineRange.intersects(reference.getTextRange())) {
+ final PsiElement psiElement = reference.resolve();
+ if (psiElement instanceof PsiVariable) {
+ final PsiVariable var = (PsiVariable)psiElement;
+ if (var instanceof PsiField) {
+ if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(reference, myVisibleLocals)) {
+ /*
+ if (var instanceof PsiEnumConstant && reference.getQualifier() == null) {
+ final PsiClass enumClass = ((PsiEnumConstant)var).getContainingClass();
+ if (enumClass != null) {
+ final PsiExpression expression = JavaPsiFacade.getInstance(var.getProject()).getParserFacade().createExpressionFromText(enumClass.getName() + "." + var.getName(), var);
+ final PsiReference ref = expression.getReference();
+ if (ref != null) {
+ ref.bindToElement(var);
+ myExpressions.add(new TextWithImportsImpl(expression));
+ }
+ }
+ }
+ else {
+ myExpressions.add(new TextWithImportsImpl(reference));
+ }
+ */
+ final PsiModifierList modifierList = var.getModifierList();
+ boolean isConstant = (var instanceof PsiEnumConstant) ||
+ (modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL));
+ if (!isConstant) {
+ myExpressions.add(new TextWithImportsImpl(reference));
+ }
+ }
+ }
+ else {
+ if (myVisibleLocals.contains(var.getName())) {
+ myVars.add(var.getName());
+ }
+ }
+ }
+ }
+ super.visitReferenceExpression(reference);
+ }
+
+ @Override
+ public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
+ if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
+ myExpressions.add(new TextWithImportsImpl(expression));
+ }
+ super.visitArrayAccessExpression(expression);
+ }
+
+ @Override
+ public void visitParameter(final PsiParameter parameter) {
+ processVariable(parameter);
+ super.visitParameter(parameter);
+ }
+
+ @Override
+ public void visitLocalVariable(final PsiLocalVariable variable) {
+ processVariable(variable);
+ super.visitLocalVariable(variable);
+ }
+
+ private void processVariable(final PsiVariable variable) {
+ if (myLineRange.intersects(variable.getTextRange()) && myVisibleLocals.contains(variable.getName())) {
+ myVars.add(variable.getName());
+ }
+ }
+
+ @Override
+ public void visitClass(final PsiClass aClass) {
+ // Do not step in to local and anonymous classes...
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FramesList.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FramesList.java
new file mode 100644
index 0000000..9967b70
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FramesList.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.xdebugger.impl.frame.DebuggerFramesList;
+import com.sun.jdi.Method;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 7, 2006
+ */
+public class FramesList extends DebuggerFramesList {
+ private volatile Method mySelectedMethod = null;
+
+ public FramesList(Project project) {
+ super(project);
+ doInit();
+ }
+
+ protected FramesListRenderer createListRenderer() {
+ return new FramesListRenderer();
+ }
+
+ protected void onFrameChanged(final Object selectedValue) {
+ final StackFrameDescriptorImpl descriptor = selectedValue instanceof StackFrameDescriptorImpl? (StackFrameDescriptorImpl)selectedValue : null;
+ final Method newMethod = descriptor != null? descriptor.getMethod() : null;
+ if (!Comparing.equal(mySelectedMethod, newMethod)) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ repaint();
+ }
+ });
+ }
+ mySelectedMethod = newMethod;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FramesListRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FramesListRenderer.java
new file mode 100644
index 0000000..c6e6228
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FramesListRenderer.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.ui.JBColor;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.ui.DebuggerColors;
+import com.sun.jdi.Method;
+
+import javax.swing.*;
+import javax.swing.border.MatteBorder;
+import java.awt.*;
+
+class FramesListRenderer extends ColoredListCellRenderer {
+ private final EditorColorsScheme myColorScheme;
+
+ public FramesListRenderer() {
+ myColorScheme = EditorColorsManager.getInstance().getGlobalScheme();
+ }
+
+ protected void customizeCellRenderer(final JList list, final Object item, final int index, final boolean selected, final boolean hasFocus) {
+ if (!(item instanceof StackFrameDescriptorImpl)) {
+ append(item.toString(), SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+ else {
+ final StackFrameDescriptorImpl descriptor = (StackFrameDescriptorImpl)item;
+ setIcon(descriptor.getIcon());
+ final Object selectedValue = list.getSelectedValue();
+ final boolean shouldHighlightAsRecursive = (selectedValue instanceof StackFrameDescriptorImpl) &&
+ isOccurrenceOfSelectedFrame((StackFrameDescriptorImpl)selectedValue, descriptor);
+
+ final ValueMarkup markup = descriptor.getValueMarkup();
+ if (markup != null) {
+ append("["+ markup.getText() + "] ", new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, markup.getColor()));
+ }
+
+ boolean needSeparator = false;
+ if (index > 0) {
+ final int currentFrameIndex = descriptor.getUiIndex();
+ final Object elementAt = list.getModel().getElementAt(index - 1);
+ if (elementAt instanceof StackFrameDescriptorImpl) {
+ StackFrameDescriptorImpl previousDescriptor = (StackFrameDescriptorImpl)elementAt;
+ final int previousFrameIndex = previousDescriptor.getUiIndex();
+ needSeparator = (currentFrameIndex - previousFrameIndex != 1);
+ }
+ }
+
+ if (selected) {
+ setBackground(UIUtil.getListSelectionBackground());
+ }
+ else {
+ Color bg = descriptor.getBackgroundColor();
+ if (bg == null) bg = UIUtil.getListBackground();
+ if (shouldHighlightAsRecursive) bg = myColorScheme.getColor(DebuggerColors.RECURSIVE_CALL_ATTRIBUTES);
+ setBackground(bg);
+ }
+
+ if (needSeparator) {
+ final MatteBorder border = BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.GRAY);
+ setBorder(border);
+ }
+ else {
+ setBorder(null);
+ }
+
+ final String label = descriptor.getLabel();
+ final int openingBrace = label.indexOf("{");
+ final int closingBrace = (openingBrace < 0) ? -1 : label.indexOf("}");
+ final SimpleTextAttributes attributes = getAttributes(descriptor);
+ if (openingBrace < 0 || closingBrace < 0) {
+ append(label, attributes);
+ }
+ else {
+ append(label.substring(0, openingBrace - 1), attributes);
+ append(" (" + label.substring(openingBrace + 1, closingBrace) + ")", SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES);
+
+ append(label.substring(closingBrace + 1, label.length()), attributes);
+ if (shouldHighlightAsRecursive && descriptor.isRecursiveCall()) {
+ append(" [" + descriptor.getOccurrenceIndex() + "]", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
+ }
+ }
+ }
+ }
+
+ private static boolean isOccurrenceOfSelectedFrame(final StackFrameDescriptorImpl selectedDescriptor, StackFrameDescriptorImpl descriptor) {
+ final Method currentMethod = descriptor.getMethod();
+ if (currentMethod != null) {
+ if (selectedDescriptor != null) {
+ final Method selectedMethod = selectedDescriptor.getMethod();
+ if (selectedMethod != null) {
+ if (Comparing.equal(selectedMethod, currentMethod)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static SimpleTextAttributes getAttributes(final StackFrameDescriptorImpl descriptor) {
+ if (descriptor.isSynthetic() || descriptor.isInLibraryContent()) {
+ return SimpleTextAttributes.GRAYED_ATTRIBUTES;
+ }
+ return SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectDebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectDebuggerTree.java
new file mode 100644
index 0000000..275e08b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectDebuggerTree.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.PopupHandler;
+
+import java.awt.*;
+
+public class InspectDebuggerTree extends DebuggerTree{
+ private NodeDescriptorImpl myInspectDescriptor;
+
+ public InspectDebuggerTree(Project project) {
+ super(project);
+
+ final PopupHandler popupHandler = new PopupHandler() {
+ public void invokePopup(Component comp, int x, int y) {
+ ActionPopupMenu popupMenu = createPopupMenu();
+ if (popupMenu != null) {
+ myTipManager.registerPopup(popupMenu.getComponent()).show(comp, x, y);
+ }
+ }
+ };
+ addMouseListener(popupHandler);
+
+ new ValueNodeDnD(this, project);
+ }
+
+ public static ActionPopupMenu createPopupMenu() {
+ ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.INSPECT_PANEL_POPUP);
+ return ActionManager.getInstance().createActionPopupMenu(DebuggerActions.INSPECT_PANEL_POPUP, group);
+ }
+
+ protected void build(DebuggerContextImpl context) {
+ updateNode(context);
+ }
+
+ public void setInspectDescriptor(NodeDescriptorImpl inspectDescriptor) {
+ myInspectDescriptor = inspectDescriptor;
+ }
+
+ public NodeDescriptorImpl getInspectDescriptor() {
+ return myInspectDescriptor;
+ }
+
+
+
+ private void updateNode(final DebuggerContextImpl context) {
+ context.getDebugProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(context) {
+ public void threadAction() {
+ final DebuggerTreeNodeImpl node = getNodeFactory().createNode(myInspectDescriptor, context.createEvaluationContext());
+
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
+ root.removeAllChildren();
+
+ root.add(node);
+ treeChanged();
+ root.getTree().expandRow(0);
+ }
+ });
+ }
+ });
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectDialog.java
new file mode 100644
index 0000000..bc2f818
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectDialog.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextListener;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+
+import javax.swing.*;
+
+public class InspectDialog extends DialogWrapper implements DebuggerContextListener {
+ private InspectPanel myInspectView;
+ private final DebuggerContextImpl myDebuggerContext;
+
+ public InspectDialog(Project project, DebuggerStateManager stateManager, String title, NodeDescriptorImpl inspectDescriptor) {
+ super(project, true);
+ setTitle(title);
+ setModal(false);
+
+ myDebuggerContext = stateManager.getContext();
+
+ myInspectView = new InspectPanel(project, myDebuggerContext.getDebuggerSession().getContextManager(), inspectDescriptor);
+ myInspectView.setBorder(BorderFactory.createEtchedBorder());
+
+ init();
+
+ myDebuggerContext.getDebuggerSession().getContextManager().addListener(this);
+ getInspectView().rebuildIfVisible(DebuggerSession.EVENT_CONTEXT);
+ }
+
+ protected JComponent createCenterPanel() {
+ return myInspectView;
+ }
+
+ protected JComponent createSouthPanel() {
+ return null;
+ }
+
+ public void dispose() {
+ myDebuggerContext.getDebuggerSession().getContextManager().removeListener(this);
+ if (myInspectView != null) {
+ myInspectView.dispose();
+ myInspectView = null;
+ }
+ super.dispose();
+ }
+
+ protected String getDimensionServiceKey(){
+ return "#com.intellij.debugger.ui.impl.InspectDialog";
+ }
+
+ public InspectPanel getInspectView() {
+ return myInspectView;
+ }
+
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ if(event == DebuggerSession.EVENT_DETACHED) {
+ close(CANCEL_EXIT_CODE);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java
new file mode 100644
index 0000000..c5fe2e0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java
@@ -0,0 +1,68 @@
+/*
+ * 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 InspectPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.actions.DebuggerAction;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.ScrollPaneFactory;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+public class InspectPanel extends DebuggerTreePanel {
+ public InspectPanel(Project project, DebuggerStateManager stateManager, @NotNull NodeDescriptorImpl inspectDescriptor) {
+ super(project, stateManager);
+
+ getInspectTree().setInspectDescriptor(inspectDescriptor);
+
+ add(ScrollPaneFactory.createScrollPane(getInspectTree()), BorderLayout.CENTER);
+ registerDisposable(DebuggerAction.installEditAction(getInspectTree(), DebuggerActions.EDIT_NODE_SOURCE));
+
+ overrideShortcut(getInspectTree(), DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy());
+ setUpdateEnabled(true);
+ }
+
+ protected void changeEvent(DebuggerContextImpl newContext, int event) {
+ if (event != DebuggerSession.EVENT_THREADS_REFRESH) {
+ super.changeEvent(newContext, event);
+ }
+ }
+
+ protected DebuggerTree createTreeView() {
+ return new InspectDebuggerTree(getProject());
+ }
+
+ protected ActionPopupMenu createPopupMenu() {
+ return InspectDebuggerTree.createPopupMenu();
+ }
+
+ public InspectDebuggerTree getInspectTree() {
+ return (InspectDebuggerTree)getTree();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/MainWatchPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/MainWatchPanel.java
new file mode 100644
index 0000000..2034938
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/MainWatchPanel.java
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Dec 17, 2001
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.actions.AddToWatchActionHandler;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.ui.DebuggerExpressionComboBox;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeInplaceEditor;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.dnd.DnDEvent;
+import com.intellij.ide.dnd.DnDManager;
+import com.intellij.ide.dnd.DnDNativeTarget;
+import com.intellij.ide.dnd.DropActionHandler;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.ui.*;
+import com.intellij.ui.border.CustomLineBorder;
+import com.intellij.xdebugger.impl.actions.XDebuggerActions;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+public class MainWatchPanel extends WatchPanel implements DataProvider {
+
+ public MainWatchPanel(Project project, DebuggerStateManager stateManager) {
+ super(project,stateManager);
+ final WatchDebuggerTree watchTree = getWatchTree();
+
+ final AnAction removeWatchesAction = ActionManager.getInstance().getAction(DebuggerActions.REMOVE_WATCH);
+ removeWatchesAction.registerCustomShortcutSet(CommonShortcuts.DELETE, watchTree);
+
+ final AnAction newWatchAction = ActionManager.getInstance().getAction(DebuggerActions.NEW_WATCH);
+ newWatchAction.registerCustomShortcutSet(CommonShortcuts.INSERT, watchTree);
+
+ final ClickListener mouseListener = new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ AnAction editWatchAction = ActionManager.getInstance().getAction(DebuggerActions.EDIT_WATCH);
+ Presentation presentation = editWatchAction.getTemplatePresentation().clone();
+ DataContext context = DataManager.getInstance().getDataContext(watchTree);
+
+ AnActionEvent actionEvent = new AnActionEvent(null, context, "WATCH_TREE", presentation, ActionManager.getInstance(), 0);
+ editWatchAction.actionPerformed(actionEvent);
+ return true;
+ }
+ };
+ ListenerUtil.addClickListener(watchTree, mouseListener);
+
+ final AnAction editWatchAction = ActionManager.getInstance().getAction(DebuggerActions.EDIT_WATCH);
+ editWatchAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)), watchTree);
+ registerDisposable(new Disposable() {
+ public void dispose() {
+ ListenerUtil.removeClickListener(watchTree, mouseListener);
+ removeWatchesAction.unregisterCustomShortcutSet(watchTree);
+ newWatchAction.unregisterCustomShortcutSet(watchTree);
+ editWatchAction.unregisterCustomShortcutSet(watchTree);
+ }
+ });
+
+ DnDManager.getInstance().registerTarget(new DnDNativeTarget() {
+ public boolean update(final DnDEvent aEvent) {
+ Object object = aEvent.getAttachedObject();
+ if (object == null) return true;
+
+ String add = DebuggerBundle.message("watchs.add.text");
+
+ if (object.getClass().isArray()) {
+ Class<?> type = object.getClass().getComponentType();
+ if (DebuggerTreeNodeImpl.class.isAssignableFrom(type)) {
+ aEvent.setHighlighting(myTree, DnDEvent.DropTargetHighlightingType.RECTANGLE | DnDEvent.DropTargetHighlightingType.TEXT);
+ aEvent.setDropPossible(add, new DropActionHandler() {
+ public void performDrop(final DnDEvent aEvent) {
+ addWatchesFrom((DebuggerTreeNodeImpl[])aEvent.getAttachedObject());
+ }
+ });
+ }
+ } else if (object instanceof EventInfo) {
+ EventInfo info = (EventInfo)object;
+ final String text = info.getTextForFlavor(DataFlavor.stringFlavor);
+ if (text != null) {
+ aEvent.setHighlighting(myTree, DnDEvent.DropTargetHighlightingType.RECTANGLE | DnDEvent.DropTargetHighlightingType.TEXT);
+ aEvent.setDropPossible(add, new DropActionHandler() {
+ public void performDrop(final DnDEvent aEvent) {
+ addWatchesFrom(text);
+ }
+ });
+ }
+ }
+
+ return true;
+ }
+
+ public void drop(final DnDEvent aEvent) {
+ }
+
+ public void cleanUpOnLeave() {
+ }
+
+ public void updateDraggedImage(final Image image, final Point dropPoint, final Point imageOffset) {
+ }
+ }, myTree);
+ }
+
+ private void addWatchesFrom(final DebuggerTreeNodeImpl[] nodes) {
+ AddToWatchActionHandler.addFromNodes(getContext(), this, nodes);
+ }
+
+ private void addWatchesFrom(String text) {
+ AddToWatchActionHandler.doAddWatch(this, new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, text), null);
+ }
+
+ protected ActionPopupMenu createPopupMenu() {
+ ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.WATCH_PANEL_POPUP);
+ ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(DebuggerActions.WATCH_PANEL_POPUP, group);
+ return popupMenu;
+ }
+
+ public void newWatch() {
+ final DebuggerTreeNodeImpl node = getWatchTree().addWatch(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""), null);
+ editNode(node);
+ }
+
+ public void editNode(final DebuggerTreeNodeImpl node) {
+ final DebuggerContextImpl context = getContext();
+ final DebuggerExpressionComboBox comboBox = new DebuggerExpressionComboBox(getProject(), PositionUtil.getContextElement(context), "evaluation",
+ DefaultCodeFragmentFactory.getInstance());
+ comboBox.setText(((WatchItemDescriptor)node.getDescriptor()).getEvaluationText());
+ comboBox.selectAll();
+
+ DebuggerTreeInplaceEditor editor = new DebuggerTreeInplaceEditor(node) {
+ public JComponent createInplaceEditorComponent() {
+ return comboBox;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return comboBox.getPreferredFocusedComponent();
+ }
+
+ public Editor getEditor() {
+ return comboBox.getEditor();
+ }
+
+ public JComponent getEditorComponent() {
+ return comboBox.getEditorComponent();
+ }
+
+ public void doOKAction() {
+ if (comboBox.isPopupVisible()) {
+ comboBox.selectPopupValue();
+ }
+
+ TextWithImports text = comboBox.getText();
+ WatchDebuggerTree.setWatchNodeText(node, text);
+ comboBox.addRecent(text);
+ try {
+ super.doOKAction();
+ }
+ finally {
+ comboBox.dispose();
+ }
+ }
+
+ public void cancelEditing() {
+ comboBox.setPopupVisible(false);
+
+ try {
+ super.cancelEditing();
+ }
+ finally {
+ comboBox.dispose();
+ }
+ }
+ };
+ editor.show();
+ }
+
+ @Override
+ protected JComponent createTreePanel(final WatchDebuggerTree tree) {
+ final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(tree);
+ decorator.setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ executeAction(DebuggerActions.NEW_WATCH, tree);
+ }
+ });
+ // TODO[den]: add "Add to watches action" on Mac
+ if (!SystemInfo.isMac) {
+ decorator.addExtraAction(AnActionButton.fromAction(ActionManager.getInstance().getAction(XDebuggerActions.ADD_TO_WATCH)));
+ }
+ decorator.setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ executeAction(DebuggerActions.REMOVE_WATCH, tree);
+ }
+ });
+ CustomLineBorder border = new CustomLineBorder(CaptionPanel.CNT_ACTIVE_BORDER_COLOR,
+ SystemInfo.isMac ? 1 : 0, 0,
+ SystemInfo.isMac ? 0 : 1, 0);
+ decorator.setToolbarBorder(border);
+ final JPanel panel = decorator.createPanel();
+ panel.setBorder(null);
+ return panel;
+ }
+
+ private static void executeAction(final String watch, final WatchDebuggerTree tree) {
+ AnAction action = ActionManager.getInstance().getAction(watch);
+ Presentation presentation = action.getTemplatePresentation().clone();
+ DataContext context = DataManager.getInstance().getDataContext(tree);
+
+ AnActionEvent actionEvent =
+ new AnActionEvent(null, context, ActionPlaces.DEBUGGER_TOOLBAR, presentation, ActionManager.getInstance(), 0);
+ action.actionPerformed(actionEvent);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java
new file mode 100644
index 0000000..18f71d5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.settings.ThreadsViewSettings;
+import com.intellij.debugger.ui.impl.watch.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.ui.tree.TreeModelAdapter;
+import com.intellij.xdebugger.XDebuggerBundle;
+
+import javax.swing.*;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.tree.TreePath;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 26, 2003
+ * Time: 5:57:58 PM
+ */
+public class ThreadsDebuggerTree extends DebuggerTree {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.ThreadsDebuggerTree");
+
+ public ThreadsDebuggerTree(Project project) {
+ super(project);
+ getEmptyText().setText(XDebuggerBundle.message("debugger.threads.not.available"));
+ }
+
+ protected NodeManagerImpl createNodeManager(Project project) {
+ return new NodeManagerImpl(project, this) {
+ public String getContextKey(StackFrameProxyImpl frame) {
+ return "ThreadsView";
+ }
+ };
+ }
+
+ protected boolean isExpandable(DebuggerTreeNodeImpl node) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if(descriptor instanceof StackFrameDescriptorImpl) {
+ return false;
+ }
+ return descriptor.isExpandable();
+ }
+
+ protected void build(DebuggerContextImpl context) {
+ DebuggerSession debuggerSession = context.getDebuggerSession();
+ final RefreshThreadsTreeCommand command = new RefreshThreadsTreeCommand(debuggerSession);
+
+ final int state = debuggerSession.getState();
+ if (ApplicationManager.getApplication().isUnitTestMode() || state == DebuggerSession.STATE_PAUSED || state == DebuggerSession.STATE_RUNNING) {
+ showMessage(MessageDescriptor.EVALUATING);
+ context.getDebugProcess().getManagerThread().schedule(command);
+ }
+ else {
+ showMessage(debuggerSession.getStateDescription());
+ }
+ }
+
+ private class RefreshThreadsTreeCommand extends DebuggerCommandImpl{
+ private final DebuggerSession mySession;
+
+ public RefreshThreadsTreeCommand(DebuggerSession session) {
+ mySession = session;
+ }
+
+ protected void action() throws Exception {
+ final DebuggerTreeNodeImpl root = getNodeFactory().getDefaultNode();
+
+ final DebugProcessImpl debugProcess = mySession.getProcess();
+ if(debugProcess == null || !debugProcess.isAttached()) {
+ return;
+ }
+ final DebuggerContextImpl context = mySession.getContextManager().getContext();
+ final SuspendContextImpl suspendContext = context.getSuspendContext();
+ final ThreadReferenceProxyImpl suspendContextThread = suspendContext != null? suspendContext.getThread() : null;
+
+ final boolean showGroups = ThreadsViewSettings.getInstance().SHOW_THREAD_GROUPS;
+ try {
+ final ThreadReferenceProxyImpl currentThread = ThreadsViewSettings.getInstance().SHOW_CURRENT_THREAD ? suspendContextThread : null;
+ final VirtualMachineProxyImpl vm = debugProcess.getVirtualMachineProxy();
+
+ final EvaluationContextImpl evaluationContext = suspendContext != null? getDebuggerContext().createEvaluationContext() : null;
+ final NodeManagerImpl nodeManager = getNodeFactory();
+
+ if (showGroups) {
+ ThreadGroupReferenceProxyImpl topCurrentGroup = null;
+
+ if (currentThread != null) {
+ topCurrentGroup = currentThread.threadGroupProxy();
+ if (topCurrentGroup != null) {
+ for(ThreadGroupReferenceProxyImpl parentGroup = topCurrentGroup.parent(); parentGroup != null; parentGroup = parentGroup.parent()) {
+ topCurrentGroup = parentGroup;
+ }
+ }
+
+ if(topCurrentGroup != null){
+ root.add(nodeManager.createNode(nodeManager.getThreadGroupDescriptor(null, topCurrentGroup), evaluationContext));
+ }
+ else {
+ root.add(nodeManager.createNode(nodeManager.getThreadDescriptor(null, currentThread), evaluationContext));
+ }
+ }
+
+ for (ThreadGroupReferenceProxyImpl group : vm.topLevelThreadGroups()) {
+ if (group != topCurrentGroup) {
+ DebuggerTreeNodeImpl threadGroup = nodeManager.createNode(nodeManager.getThreadGroupDescriptor(null, group), evaluationContext);
+ root.add(threadGroup);
+ }
+ }
+ }
+ else {
+ // do not show thread groups
+ if (currentThread != null) {
+ root.insert(nodeManager.createNode(nodeManager.getThreadDescriptor(null, currentThread), evaluationContext), 0);
+ }
+ List<ThreadReferenceProxyImpl> allThreads = new ArrayList<ThreadReferenceProxyImpl>(vm.allThreads());
+ Collections.sort(allThreads, ThreadReferenceProxyImpl.ourComparator);
+
+ for (ThreadReferenceProxyImpl threadProxy : allThreads) {
+ if (threadProxy.equals(currentThread)) {
+ continue;
+ }
+ root.add(nodeManager.createNode(nodeManager.getThreadDescriptor(null, threadProxy), evaluationContext));
+ }
+ }
+ }
+ catch (Exception ex) {
+ root.add( MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(ex);
+ }
+ }
+
+ final boolean hasThreadToSelect = suspendContextThread != null; // thread can be null if pause was pressed
+ final List<ThreadGroupReferenceProxyImpl> groups;
+ if (hasThreadToSelect && showGroups) {
+ groups = new ArrayList<ThreadGroupReferenceProxyImpl>();
+ for(ThreadGroupReferenceProxyImpl group = suspendContextThread.threadGroupProxy(); group != null; group = group.parent()) {
+ groups.add(group);
+ }
+ Collections.reverse(groups);
+ }
+ else {
+ groups = Collections.emptyList();
+ }
+
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ getMutableModel().setRoot(root);
+ treeChanged();
+ if (hasThreadToSelect) {
+ selectThread(groups, suspendContextThread, true);
+ }
+ }
+ });
+ }
+
+ private void selectThread(final List<ThreadGroupReferenceProxyImpl> pathToThread, final ThreadReferenceProxyImpl thread, final boolean expand) {
+ LOG.assertTrue(SwingUtilities.isEventDispatchThread());
+ class MyTreeModelAdapter extends TreeModelAdapter {
+ private void structureChanged(DebuggerTreeNodeImpl node) {
+ for(Enumeration enumeration = node.children(); enumeration.hasMoreElements(); ) {
+ DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)enumeration.nextElement();
+ nodeChanged(child);
+ }
+ }
+
+ private void nodeChanged(DebuggerTreeNodeImpl debuggerTreeNode) {
+ if(pathToThread.size() == 0) {
+ if(debuggerTreeNode.getDescriptor() instanceof ThreadDescriptorImpl && ((ThreadDescriptorImpl) debuggerTreeNode.getDescriptor()).getThreadReference() == thread) {
+ removeListener();
+ final TreePath treePath = new TreePath(debuggerTreeNode.getPath());
+ setSelectionPath(treePath);
+ if (expand && !isExpanded(treePath)) {
+ expandPath(treePath);
+ }
+ }
+ }
+ else {
+ if(debuggerTreeNode.getDescriptor() instanceof ThreadGroupDescriptorImpl && ((ThreadGroupDescriptorImpl) debuggerTreeNode.getDescriptor()).getThreadGroupReference() == pathToThread.get(0)) {
+ pathToThread.remove(0);
+ expandPath(new TreePath(debuggerTreeNode.getPath()));
+ }
+ }
+ }
+
+ private void removeListener() {
+ final TreeModelAdapter listener = this;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ getModel().removeTreeModelListener(listener);
+ }
+ });
+ }
+
+ public void treeStructureChanged(TreeModelEvent event) {
+ if(event.getPath().length <= 1) {
+ removeListener();
+ return;
+ }
+ structureChanged((DebuggerTreeNodeImpl)event.getTreePath().getLastPathComponent());
+ }
+ }
+
+ MyTreeModelAdapter listener = new MyTreeModelAdapter();
+ listener.structureChanged((DebuggerTreeNodeImpl)getModel().getRoot());
+ getModel().addTreeModelListener(listener);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java
new file mode 100644
index 0000000..f6d2116
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.actions.DebuggerAction;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.*;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.util.Alarm;
+import org.jetbrains.annotations.NonNls;
+
+import java.awt.*;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+public class ThreadsPanel extends DebuggerTreePanel{
+ @NonNls private static final String HELP_ID = "debugging.debugThreads";
+ private final Alarm myUpdateLabelsAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+ private static final int LABELS_UPDATE_DELAY_MS = 200;
+
+ public ThreadsPanel(Project project, final DebuggerStateManager stateManager) {
+ super(project, stateManager);
+
+ final Disposable disposable = DebuggerAction.installEditAction(getThreadsTree(), DebuggerActions.EDIT_FRAME_SOURCE);
+ registerDisposable(disposable);
+
+ getThreadsTree().addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER && getThreadsTree().getSelectionCount() == 1) {
+ DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)getThreadsTree().getLastSelectedPathComponent();
+ if (node != null) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (descriptor instanceof StackFrameDescriptorImpl) {
+ selectFrame(node);
+ }
+ }
+ }
+ }
+ });
+ add(ScrollPaneFactory.createScrollPane(getThreadsTree()), BorderLayout.CENTER);
+ stateManager.addListener(new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ if (DebuggerSession.EVENT_ATTACHED == event || DebuggerSession.EVENT_RESUME == event) {
+ startLabelsUpdate();
+ }
+ else if (DebuggerSession.EVENT_PAUSE == event || DebuggerSession.EVENT_DETACHED == event || DebuggerSession.EVENT_DISPOSE == event) {
+ myUpdateLabelsAlarm.cancelAllRequests();
+ }
+ if (DebuggerSession.EVENT_DETACHED == event || DebuggerSession.EVENT_DISPOSE == event) {
+ stateManager.removeListener(this);
+ }
+ }
+ });
+ startLabelsUpdate();
+ }
+
+ private void startLabelsUpdate() {
+ myUpdateLabelsAlarm.cancelAllRequests();
+ myUpdateLabelsAlarm.addRequest(new Runnable() {
+ public void run() {
+ boolean updateScheduled = false;
+ try {
+ if (isUpdateEnabled()) {
+ final ThreadsDebuggerTree tree = getThreadsTree();
+ final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)tree.getModel().getRoot();
+ if (root != null) {
+ final DebugProcessImpl process = getContext().getDebugProcess();
+ if (process != null) {
+ process.getManagerThread().invoke(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ try {
+ updateNodeLabels(root);
+ }
+ finally {
+ reschedule();
+ }
+ }
+ protected void commandCancelled() {
+ reschedule();
+ }
+ });
+ updateScheduled = true;
+ }
+ }
+ }
+ }
+ finally {
+ if (!updateScheduled) {
+ reschedule();
+ }
+ }
+ }
+
+ private void reschedule() {
+ final DebuggerSession session = getContext().getDebuggerSession();
+ if (session.isAttached() && !session.isPaused()) {
+ myUpdateLabelsAlarm.addRequest(this, LABELS_UPDATE_DELAY_MS, ModalityState.NON_MODAL);
+ }
+ }
+
+ }, LABELS_UPDATE_DELAY_MS, ModalityState.NON_MODAL);
+ }
+
+ @Override
+ public void dispose() {
+ Disposer.dispose(myUpdateLabelsAlarm);
+ super.dispose();
+ }
+
+ private static void updateNodeLabels(DebuggerTreeNodeImpl from) {
+ final int childCount = from.getChildCount();
+ for (int idx = 0; idx < childCount; idx++) {
+ final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)from.getChildAt(idx);
+ child.getDescriptor().updateRepresentation(null, new DescriptorLabelListener() {
+ public void labelChanged() {
+ child.labelChanged();
+ }
+ });
+ updateNodeLabels(child);
+ }
+ }
+
+ protected DebuggerTree createTreeView() {
+ return new ThreadsDebuggerTree(getProject());
+ }
+
+ protected ActionPopupMenu createPopupMenu() {
+ DefaultActionGroup group = (DefaultActionGroup)ActionManager.getInstance().getAction(DebuggerActions.THREADS_PANEL_POPUP);
+ return ActionManager.getInstance().createActionPopupMenu(DebuggerActions.THREADS_PANEL_POPUP, group);
+ }
+
+ public Object getData(String dataId) {
+ if (PlatformDataKeys.HELP_ID.is(dataId)) {
+ return HELP_ID;
+ }
+ return super.getData(dataId);
+ }
+
+ private void selectFrame(DebuggerTreeNodeImpl node) {
+ StackFrameProxyImpl frame = ((StackFrameDescriptorImpl)node.getDescriptor()).getFrameProxy();
+ DebuggerContextUtil.setStackFrame(getContextManager(), frame);
+ }
+
+ public ThreadsDebuggerTree getThreadsTree() {
+ return (ThreadsDebuggerTree) getTree();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/TipManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/TipManager.java
new file mode 100644
index 0000000..5a8f0c1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/TipManager.java
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.ide.FrameStateListener;
+import com.intellij.ide.FrameStateManager;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.wm.IdeGlassPane;
+import com.intellij.openapi.wm.IdeGlassPaneUtil;
+import com.intellij.util.Alarm;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.update.Activatable;
+import com.intellij.util.ui.update.UiNotifyConnector;
+
+import javax.swing.*;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * @author lex
+ */
+public class TipManager implements Disposable, PopupMenuListener {
+
+ private volatile boolean myIsDisposed = false;
+ private boolean myPopupShown;
+ private MyAwtPreprocessor myHideCanceller;
+
+ private MouseEvent myLastMouseEvent;
+
+ public interface TipFactory {
+ JComponent createToolTip (MouseEvent e);
+ MouseEvent createTooltipEvent(MouseEvent candidateEvent);
+ boolean isFocusOwner();
+ }
+
+
+ private boolean isOverTip(MouseEvent e) {
+ if (myCurrentTooltip != null) {
+ if(!myCurrentTooltip.isShowing()) {
+ hideTooltip(true);
+ return false;
+ }
+ final Component eventOriginator = e.getComponent();
+ if (eventOriginator == null) {
+ return false;
+ }
+ final Point point = e.getPoint();
+ SwingUtilities.convertPointToScreen(point, eventOriginator);
+
+ final Rectangle bounds = myCurrentTooltip.getBounds();
+ final Point tooltipLocationOnScreen = myCurrentTooltip.getLocationOnScreen();
+ bounds.setLocation(tooltipLocationOnScreen.x, tooltipLocationOnScreen.y);
+
+ return bounds.contains(point);
+ }
+ return false;
+ }
+
+ boolean myInsideComponent;
+
+ private class MyMouseListener extends MouseAdapter {
+ @Override
+ public void mouseExited(final MouseEvent e) {
+ myInsideComponent = false;
+ }
+
+ @Override
+ public void mousePressed(final MouseEvent e) {
+ if (myInsideComponent) {
+ hideTooltip(true);
+ }
+ }
+
+ @Override
+ public void mouseEntered(final MouseEvent e) {
+ myInsideComponent = true;
+ }
+ }
+
+ private class MyFrameStateListener implements FrameStateListener {
+ @Override
+ public void onFrameDeactivated() {
+ hideTooltip(true);
+ }
+
+ @Override
+ public void onFrameActivated() {
+ //Do nothing
+ }
+ }
+
+ public JPopupMenu registerPopup(JPopupMenu menu) {
+ menu.addPopupMenuListener(this);
+ return menu;
+ }
+
+ public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
+ myPopupShown = true;
+ }
+
+ public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
+ onPopupClosed(e);
+ }
+
+ public void popupMenuCanceled(final PopupMenuEvent e) {
+ onPopupClosed(e);
+ }
+
+ private void onPopupClosed(final PopupMenuEvent e) {
+ myPopupShown = false;
+ if (e.getSource() instanceof JPopupMenu) {
+ ((JPopupMenu)e.getSource()).removePopupMenuListener(this);
+ }
+ }
+
+ private class MyMouseMotionListener extends MouseMotionAdapter {
+ @Override
+ public void mouseMoved(final MouseEvent e) {
+ myLastMouseEvent = e;
+
+ if (!myComponent.isShowing()) return;
+
+ myInsideComponent = true;
+
+ if (myCurrentTooltip == null) {
+ if (isInsideComponent(e)) {
+ tryTooltip(e, true);
+ }
+ } else {
+ if (!isOverTip(e)) {
+ tryTooltip(e, true);
+ }
+ }
+ }
+
+ }
+
+ private boolean isInsideComponent(final MouseEvent e) {
+ final Rectangle compBounds = myComponent.getBounds();
+ final Point compPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), myComponent);
+
+ return compBounds.contains(compPoint);
+ }
+
+
+ private void tryTooltip(final InputEvent e, final boolean auto) {
+ myShowAlarm.cancelAllRequests();
+ myHideAlarm.cancelAllRequests();
+ myShowAlarm.addRequest(new Runnable() {
+ public void run() {
+ if (!myIsDisposed && !myPopupShown) {
+ showTooltip(e, auto);
+ }
+ }
+ }, auto ? DebuggerSettings.getInstance().VALUE_LOOKUP_DELAY : 10);
+ }
+
+ private void showTooltip(InputEvent e, boolean auto) {
+ if (auto && !Registry.is("debugger.valueTooltipAutoShow")) return;
+
+ MouseEvent sourceEvent = null;
+ JComponent newTip = null;
+
+ if (e instanceof MouseEvent) {
+ sourceEvent = (MouseEvent)e;
+ } else if (e instanceof KeyEvent) {
+ sourceEvent = myTipFactory.createTooltipEvent(myLastMouseEvent);
+ }
+
+
+ MouseEvent convertedEvent = null;
+ if (sourceEvent != null) {
+ convertedEvent = SwingUtilities.convertMouseEvent(sourceEvent.getComponent(), sourceEvent, myComponent);
+ newTip = myTipFactory.createToolTip(convertedEvent);
+ }
+
+ if (newTip == null || (auto && !myTipFactory.isFocusOwner())) {
+ hideTooltip(false);
+ return;
+ }
+
+ if(newTip == myCurrentTooltip) {
+ if (!auto) {
+ hideTooltip(true);
+ return;
+ }
+ return;
+ }
+
+ hideTooltip(true);
+
+ if(myComponent.isShowing()) {
+ PopupFactory popupFactory = PopupFactory.getSharedInstance();
+ final Point location = convertedEvent.getPoint();
+ final Component sourceComponent = convertedEvent.getComponent();
+ if (sourceComponent != null) {
+ SwingUtilities.convertPointToScreen(location, sourceComponent);
+ }
+
+ myTipPopup = popupFactory.getPopup(myComponent, newTip, location.x, location.y);
+ myInsideComponent = false;
+ myTipPopup.show();
+ myCurrentTooltip = newTip;
+ }
+ }
+
+ public void hideTooltip() {
+ hideTooltip(true);
+ }
+
+ public void hideTooltip(boolean now) {
+ if (myTipPopup == null) return;
+
+ if (now) {
+ myHideAlarm.cancelAllRequests();
+ myTipPopup.hide();
+ myTipPopup = null;
+ myCurrentTooltip = null;
+ } else {
+ myHideAlarm.addRequest(new Runnable() {
+ public void run() {
+ if (myInsideComponent) {
+ hideTooltip(true);
+ }
+ }
+ }, 100);
+ }
+ }
+
+ private JComponent myCurrentTooltip;
+ private Popup myTipPopup;
+ private final TipFactory myTipFactory;
+ private final JComponent myComponent;
+ private MouseListener myMouseListener = new MyMouseListener();
+ private MouseMotionListener myMouseMotionListener = new MyMouseMotionListener();
+ private FrameStateListener myFrameStateListener = new MyFrameStateListener();
+
+ private final Alarm myShowAlarm = new Alarm();
+ private final Alarm myHideAlarm = new Alarm();
+
+
+ private IdeGlassPane myGP;
+
+ public TipManager(final JComponent component, TipFactory factory) {
+ myTipFactory = factory;
+ myComponent = component;
+
+ new UiNotifyConnector.Once(component, new Activatable() {
+ public void showNotify() {
+ installListeners();
+ }
+
+ public void hideNotify() {
+ }
+ });
+
+ final HideTooltipAction hide = new HideTooltipAction();
+ hide.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)), myComponent);
+ Disposer.register(this, new Disposable() {
+ public void dispose() {
+ hide.unregisterCustomShortcutSet(myComponent);
+ }
+ });
+ }
+
+
+ private class HideTooltipAction extends AnAction {
+ public void actionPerformed(AnActionEvent e) {
+ hideTooltip(true);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(myTipPopup != null);
+ }
+ }
+
+ private void installListeners() {
+ if (myIsDisposed) return;
+
+ myGP = IdeGlassPaneUtil.find(myComponent);
+ assert myGP != null;
+
+ myGP.addMousePreprocessor(myMouseListener, this);
+ myGP.addMouseMotionPreprocessor(myMouseMotionListener, this);
+
+ myHideCanceller = new MyAwtPreprocessor();
+ Toolkit.getDefaultToolkit().addAWTEventListener(myHideCanceller, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
+ FrameStateManager.getInstance().addListener(myFrameStateListener);
+ }
+
+ public void dispose() {
+ Disposer.dispose(this);
+
+ hideTooltip(true);
+
+ Toolkit.getDefaultToolkit().removeAWTEventListener(myHideCanceller);
+
+ myIsDisposed = true;
+ myShowAlarm.cancelAllRequests();
+ myMouseListener = null;
+ myMouseMotionListener = null;
+ FrameStateManager.getInstance().removeListener(myFrameStateListener);
+ myFrameStateListener = null;
+ }
+
+ private class MyAwtPreprocessor implements AWTEventListener {
+
+ public void eventDispatched(AWTEvent event) {
+ if (event.getID() == MouseEvent.MOUSE_MOVED) {
+ preventFromHideIfInsideTooltip(event);
+ } else if (event.getID() == MouseEvent.MOUSE_PRESSED || event.getID() == MouseEvent.MOUSE_RELEASED) {
+ hideTooltipIfCloseClick((MouseEvent)event);
+ } else if (event instanceof KeyEvent) {
+ tryToShowTooltipIfRequested((KeyEvent)event);
+ }
+ }
+
+ private void hideTooltipIfCloseClick(MouseEvent me) {
+ if (myCurrentTooltip == null) return;
+
+ if (isInsideTooltip(me) && UIUtil.isCloseClick(me)) {
+ hideTooltip(true);
+ }
+ }
+
+ private void tryToShowTooltipIfRequested(KeyEvent event) {
+ if (KeymapUtil.isTooltipRequest(event)) {
+ tryTooltip(event, false);
+ } else {
+ if (event.getID() == KeyEvent.KEY_PRESSED) {
+ myLastMouseEvent = null;
+ }
+ }
+ }
+
+ private void preventFromHideIfInsideTooltip(AWTEvent event) {
+ if (myCurrentTooltip == null) return;
+
+ if (event.getID() == MouseEvent.MOUSE_MOVED) {
+ final MouseEvent me = (MouseEvent)event;
+ if (isInsideTooltip(me)) {
+ myHideAlarm.cancelAllRequests();
+ }
+ }
+ }
+
+ private boolean isInsideTooltip(MouseEvent me) {
+ return myCurrentTooltip == me.getComponent() || SwingUtilities.isDescendingFrom(me.getComponent(), myCurrentTooltip);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java
new file mode 100644
index 0000000..97a5fd8
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerContextListener;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.DebuggerView;
+import com.intellij.openapi.CompositeDisposable;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.ShortcutSet;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+
+import javax.swing.*;
+import java.awt.*;
+
+public abstract class UpdatableDebuggerView extends JPanel implements DebuggerView {
+ private final Project myProject;
+ private final DebuggerStateManager myStateManager;
+ private volatile boolean myRefreshNeeded = true;
+ private final CompositeDisposable myDisposables = new CompositeDisposable();
+ private volatile boolean myUpdateEnabled;
+
+ protected UpdatableDebuggerView(final Project project, final DebuggerStateManager stateManager) {
+ setLayout(new BorderLayout());
+ myProject = project;
+ myStateManager = stateManager;
+
+ final DebuggerContextListener contextListener = new DebuggerContextListener() {
+ public void changeEvent(DebuggerContextImpl newContext, int event) {
+ UpdatableDebuggerView.this.changeEvent(newContext, event);
+ }
+ };
+ myStateManager.addListener(contextListener);
+
+ registerDisposable(new Disposable() {
+ public void dispose() {
+ myStateManager.removeListener(contextListener);
+ }
+ });
+
+ }
+
+ protected void changeEvent(final DebuggerContextImpl newContext, final int event) {
+ if (newContext.getDebuggerSession() != null) {
+ rebuildIfVisible(event);
+ }
+ }
+
+ protected final boolean isUpdateEnabled() {
+ return myUpdateEnabled || isShowing();
+ }
+
+ public final void setUpdateEnabled(final boolean enabled) {
+ myUpdateEnabled = enabled;
+ }
+
+ public final boolean isRefreshNeeded() {
+ return myRefreshNeeded;
+ }
+
+ public final void rebuildIfVisible(final int event) {
+ if(isUpdateEnabled()) {
+ myRefreshNeeded = false;
+ rebuild(event);
+ }
+ else {
+ myRefreshNeeded = true;
+ }
+ }
+
+ protected abstract void rebuild(int event);
+
+ protected final void registerDisposable(Disposable disposable) {
+ myDisposables.add(disposable);
+ }
+
+ public DebuggerContextImpl getContext() {
+ return myStateManager.getContext();
+ }
+
+ protected final Project getProject() {
+ return myProject;
+ }
+
+ public DebuggerStateManager getContextManager() {
+ return myStateManager;
+ }
+
+ public void dispose() {
+ Disposer.dispose(myDisposables);
+ }
+
+ protected void overrideShortcut(final JComponent forComponent, final String actionId, final ShortcutSet shortcutSet) {
+ final AnAction action = ActionManager.getInstance().getAction(actionId);
+ action.registerCustomShortcutSet(shortcutSet, forComponent);
+ registerDisposable(new Disposable() {
+ public void dispose() {
+ action.unregisterCustomShortcutSet(forComponent);
+ }
+ });
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ValueNodeDnD.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ValueNodeDnD.java
new file mode 100644
index 0000000..cb006ae
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ValueNodeDnD.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.ide.dnd.DnDAction;
+import com.intellij.ide.dnd.DnDDragStartBean;
+import com.intellij.ide.dnd.DnDManager;
+import com.intellij.ide.dnd.DnDSource;
+import com.intellij.ide.dnd.aware.DnDAwareTree;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.ui.treeStructure.Tree;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.tree.TreePath;
+import java.awt.*;
+
+public class ValueNodeDnD {
+ private final DnDAwareTree myTree;
+
+ public ValueNodeDnD(DnDAwareTree tree, Disposable parent) {
+ myTree = tree;
+
+ DnDManager.getInstance().registerSource(new DnDSource() {
+ public boolean canStartDragging(final DnDAction action, final Point dragOrigin) {
+ return getNodesToDrag().length > 0;
+ }
+
+ public DnDDragStartBean startDragging(final DnDAction action, final Point dragOrigin) {
+ DebuggerTreeNodeImpl[] nodes = getNodesToDrag();
+ return new DnDDragStartBean(nodes);
+ }
+
+ @Nullable
+ public Pair<Image, Point> createDraggedImage(final DnDAction action, final Point dragOrigin) {
+ DebuggerTreeNodeImpl[] nodes = getNodesToDrag();
+
+ Pair<Image, Point> image;
+ if (nodes.length == 1) {
+ image = DnDAwareTree.getDragImage(myTree, new TreePath(nodes[0].getPath()), dragOrigin);
+ } else {
+ image = DnDAwareTree.getDragImage(myTree, nodes.length + " elements", dragOrigin);
+ }
+
+ return image;
+ }
+
+ public void dragDropEnd() {
+ }
+
+ public void dropActionChanged(final int gestureModifiers) {
+ }
+ }, tree);
+ }
+
+ private DebuggerTreeNodeImpl[] getNodesToDrag() {
+ return myTree.getSelectedNodes(DebuggerTreeNodeImpl.class, new Tree.NodeFilter<DebuggerTreeNodeImpl>() {
+ public boolean accept(final DebuggerTreeNodeImpl node) {
+ return node.getDescriptor() instanceof ValueDescriptorImpl;
+ }
+ });
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java
new file mode 100644
index 0000000..af48223
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.actions.DebuggerAction;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.ScrollPaneFactory;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+
+public class VariablesPanel extends DebuggerTreePanel implements DataProvider{
+
+ @NonNls private static final String HELP_ID = "debugging.debugFrame";
+
+ public VariablesPanel(Project project, DebuggerStateManager stateManager, Disposable parent) {
+ super(project, stateManager);
+ setBorder(null);
+
+
+ final FrameVariablesTree frameTree = getFrameTree();
+
+ add(ScrollPaneFactory.createScrollPane(frameTree), BorderLayout.CENTER);
+ registerDisposable(DebuggerAction.installEditAction(frameTree, DebuggerActions.EDIT_NODE_SOURCE));
+
+ overrideShortcut(frameTree, DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy());
+ overrideShortcut(frameTree, DebuggerActions.SET_VALUE, new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)));
+
+ new ValueNodeDnD(myTree, parent);
+ }
+
+ protected DebuggerTree createTreeView() {
+ return new FrameVariablesTree(getProject());
+ }
+
+ protected void changeEvent(DebuggerContextImpl newContext, int event) {
+ if (event != DebuggerSession.EVENT_THREADS_REFRESH) {
+ super.changeEvent(newContext, event);
+ }
+ }
+
+ protected ActionPopupMenu createPopupMenu() {
+ ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.FRAME_PANEL_POPUP);
+ return ActionManager.getInstance().createActionPopupMenu(DebuggerActions.FRAME_PANEL_POPUP, group);
+ }
+
+ public Object getData(String dataId) {
+ if (PlatformDataKeys.HELP_ID.is(dataId)) {
+ return HELP_ID;
+ }
+ return super.getData(dataId);
+ }
+
+
+ public FrameVariablesTree getFrameTree() {
+ return (FrameVariablesTree) getTree();
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchDebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchDebuggerTree.java
new file mode 100644
index 0000000..8282adf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchDebuggerTree.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.XDebuggerBundle;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.tree.TreePath;
+import java.util.Enumeration;
+
+public class WatchDebuggerTree extends DebuggerTree {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.WatchDebuggerTree");
+
+ public WatchDebuggerTree(Project project) {
+ super(project);
+ getEmptyText().setText(XDebuggerBundle.message("debugger.no.watches"));
+ }
+
+ public DebuggerTreeNodeImpl[] getWatches() {
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
+ DebuggerTreeNodeImpl[] watches = new DebuggerTreeNodeImpl[root.getChildCount()];
+
+ final Enumeration e = root.children();
+ int i = 0;
+ while(e.hasMoreElements()) {
+ watches[i++] = (DebuggerTreeNodeImpl)e.nextElement();
+ }
+
+ return watches;
+ }
+
+ public int getWatchCount() {
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
+ return root != null ? root.getChildCount() : 0;
+ }
+
+ public DebuggerTreeNodeImpl addWatch(WatchItemDescriptor descriptor) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
+ WatchItemDescriptor watchDescriptor = new WatchItemDescriptor(getProject(), descriptor.getEvaluationText());
+ watchDescriptor.displayAs(descriptor);
+
+ final DebuggerTreeNodeImpl node = DebuggerTreeNodeImpl.createNodeNoUpdate(this, watchDescriptor);
+ root.add(node);
+
+ treeChanged();
+ getSelectionModel().setSelectionPath(new TreePath(node.getPath()));
+
+ //node.calcValue();
+
+ return node;
+ }
+
+ public DebuggerTreeNodeImpl addWatch(TextWithImports text, @Nullable String customName) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
+ final WatchItemDescriptor descriptor = new WatchItemDescriptor(getProject(), text);
+ descriptor.setCustomName(customName);
+ DebuggerTreeNodeImpl node = DebuggerTreeNodeImpl.createNodeNoUpdate(this, descriptor);
+ root.add(node);
+
+ treeChanged();
+ final TreePath path = new TreePath(node.getPath());
+ getSelectionModel().setSelectionPath(path);
+ scrollPathToVisible(path);
+ return node;
+ }
+
+ public void removeWatch(DebuggerTreeNodeImpl node) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ LOG.assertTrue(node.getDescriptor() instanceof WatchItemDescriptor);
+
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
+ DebuggerTreeNodeImpl nodeToSelect = (DebuggerTreeNodeImpl) node.getNextSibling();
+
+ getMutableModel().removeNodeFromParent(node);
+ treeChanged();
+
+ if(nodeToSelect == null && root.getChildCount() > 0) {
+ nodeToSelect = (DebuggerTreeNodeImpl) root.getChildAt(root.getChildCount() - 1);
+ }
+
+ if(nodeToSelect != null) {
+ setSelectionPath(new TreePath(nodeToSelect.getPath()));
+ }
+ }
+
+ protected void build(DebuggerContextImpl context) {
+ for (DebuggerTreeNodeImpl node : getWatches()) {
+ node.calcValue();
+ }
+ }
+
+ public static void setWatchNodeText(final DebuggerTreeNodeImpl node, TextWithImports text) {
+ ((WatchItemDescriptor)node.getDescriptor()).setEvaluationText(text);
+ node.calcValue();
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java
new file mode 100644
index 0000000..8d9cefc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java
@@ -0,0 +1,91 @@
+/*
+ * 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 WatchPanel
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl;
+
+import com.intellij.debugger.actions.DebuggerAction;
+import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.impl.DebuggerStateManager;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
+import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.ScrollPaneFactory;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Enumeration;
+
+public abstract class WatchPanel extends DebuggerTreePanel {
+ @NonNls private static final String HELP_ID = "debugging.debugWatches";
+
+ public WatchPanel(Project project, DebuggerStateManager stateManager) {
+ super(project, stateManager);
+ add(createTreePanel(getWatchTree()), BorderLayout.CENTER);
+ registerDisposable(DebuggerAction.installEditAction(getWatchTree(), DebuggerActions.EDIT_NODE_SOURCE));
+ overrideShortcut(getWatchTree(), DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy());
+ }
+
+ protected JComponent createTreePanel(final WatchDebuggerTree tree) {
+ return ScrollPaneFactory.createScrollPane(tree);
+ }
+
+ protected DebuggerTree createTreeView() {
+ return new WatchDebuggerTree(getProject());
+ }
+
+ protected void changeEvent(DebuggerContextImpl newContext, int event) {
+ if (event == DebuggerSession.EVENT_THREADS_REFRESH) {
+ return;
+ }
+ if(event == DebuggerSession.EVENT_ATTACHED) {
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getWatchTree().getModel().getRoot();
+ if(root != null) {
+ for(Enumeration e = root.rawChildren(); e.hasMoreElements();) {
+ DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl) e.nextElement();
+ ((WatchItemDescriptor) child.getDescriptor()).setNew();
+ }
+ }
+ }
+
+ rebuildIfVisible(event);
+ }
+
+ protected ActionPopupMenu createPopupMenu() {
+ return null;
+ }
+
+ public Object getData(String dataId) {
+ if (PlatformDataKeys.HELP_ID.is(dataId)) {
+ return HELP_ID;
+ }
+ return super.getData(dataId);
+ }
+
+ public WatchDebuggerTree getWatchTree() {
+ return (WatchDebuggerTree) getTree();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/nodes/ArrayIndexHelper.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/nodes/ArrayIndexHelper.java
new file mode 100644
index 0000000..f828a0e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/nodes/ArrayIndexHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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 ArrayIndexHelper
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.nodes;
+
+import com.intellij.debugger.ui.tree.render.ArrayRenderer;
+import com.intellij.debugger.ui.tree.render.ArrayRenderer;
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.ArrayReference;
+
+public class ArrayIndexHelper {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.nodes.ArrayIndexHelper");
+ private final ArrayReference myArray;
+ private final ArrayRenderer myRenderer;
+
+ public ArrayIndexHelper(ArrayReference array, ArrayRenderer renderer) {
+ myRenderer = renderer;
+ myArray = array;
+ }
+
+ /**
+ * @return normalized start index or -1 if this is not an array or array's length == 0
+ */
+ public int getStartIndex() {
+ if (myArray.length() == 0) return -1;
+ return myRenderer.START_INDEX;
+ }
+
+ /**
+ * @return normalized end index or -1 if this is not an array or array's length == 0
+ */
+
+ public int getEndIndex() {
+ return Math.min(myArray.length() - 1, myRenderer.END_INDEX);
+ }
+
+ public ArrayRenderer newRenderer(int startIdx, int endIdx) {
+ ArrayRenderer result = myRenderer.clone();
+ result.START_INDEX = startIdx < myArray.length() ? startIdx : 0;
+ result.END_INDEX = startIdx <= endIdx ? endIdx : startIdx;
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/nodes/NodeComparator.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/nodes/NodeComparator.java
new file mode 100644
index 0000000..4c2a694
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/nodes/NodeComparator.java
@@ -0,0 +1,51 @@
+/*
+ * 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 NodeComparator
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.nodes;
+
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+
+import java.util.Comparator;
+
+/**
+ * Compares given DebuggerTreeTest by name
+ */
+public class NodeComparator implements Comparator<DebuggerTreeNode> {
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public int compare(final DebuggerTreeNode node1, final DebuggerTreeNode node2) {
+ final String name1 = node1.getDescriptor().getName();
+ final String name2 = node2.getDescriptor().getName();
+ final boolean invalid1 = (name1 == null || (name1.length() > 0 && Character.isDigit(name1.charAt(0))));
+ final boolean invalid2 = (name2 == null || (name2.length() > 0 && Character.isDigit(name2.charAt(0))));
+ if (invalid1) {
+ return invalid2? 0 : 1;
+ }
+ else if (invalid2) {
+ return -1;
+ }
+ if ("this".equals(name1) || "static".equals(name1)) {
+ return -1;
+ }
+ if ("this".equals(name2) || "static".equals(name2)) {
+ return 1;
+ }
+ return name1.compareToIgnoreCase(name2);
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/tree/TreeBuilder.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/tree/TreeBuilder.java
new file mode 100644
index 0000000..9686603
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/tree/TreeBuilder.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.tree;
+
+import com.intellij.util.EventDispatcher;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+/**
+ * User: lex
+ * Date: Sep 10, 2003
+ * Time: 6:56:51 PM
+ */
+public abstract class TreeBuilder implements TreeModel {
+ private final Object userObject;
+ private TreeBuilderNode myRoot;
+ private final EventDispatcher<TreeModelListener> myDispatcher = EventDispatcher.create(TreeModelListener.class);
+
+ protected TreeBuilder(Object userObject) {
+ this.userObject = userObject;
+ }
+
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ public abstract void buildChildren(TreeBuilderNode node);
+ public abstract boolean isExpandable (TreeBuilderNode node);
+
+ public void setRoot(TreeBuilderNode root) {
+ myRoot = root;
+ }
+
+ public Object getRoot() {
+ return myRoot;
+ }
+
+ public int getChildCount(Object parent) {
+ return ((TreeBuilderNode) parent).getChildCount();
+ }
+
+ public boolean isLeaf(Object node) {
+ return ((TreeBuilderNode) node).isLeaf();
+ }
+
+ public void addTreeModelListener(TreeModelListener l) {
+ myDispatcher.addListener(l);
+ }
+
+ public void removeTreeModelListener(TreeModelListener l) {
+ myDispatcher.removeListener(l);
+ }
+
+ public Object getChild(Object parent, int index) {
+ return ((TreeBuilderNode) parent).getChildAt(index);
+ }
+
+ public int getIndexOfChild(Object parent, Object child) {
+ return ((TreeBuilderNode) parent).getIndex((TreeNode) child);
+ }
+
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ TreeBuilderNode aNode = (TreeBuilderNode) path.getLastPathComponent();
+
+ aNode.setUserObject(newValue);
+ nodeChanged(aNode);
+ }
+
+ public void nodeChanged(TreeNode node) {
+ TreeModelEvent event = null;
+ TreeNode parent = node.getParent();
+ if (parent != null) {
+ int anIndex = parent.getIndex(node);
+ event = new TreeModelEvent(this, getPathToRoot(parent, 0), new int[] {anIndex}, new Object[] {node});
+ } else if (node == getRoot()) {
+ event = new TreeModelEvent(this, getPathToRoot(node, 0), null, null);
+ }
+ if (event != null) {
+ myDispatcher.getMulticaster().treeNodesChanged(event);
+ }
+ }
+
+ public void nodeStructureChanged(TreeNode node) {
+ TreeModelEvent event = new TreeModelEvent(this, getPathToRoot(node, 0), null, null);
+ myDispatcher.getMulticaster().treeStructureChanged(event);
+ }
+
+ protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
+ TreeNode[] retNodes;
+ if(aNode == null) {
+ if(depth == 0)
+ return null;
+ else
+ retNodes = new TreeNode[depth];
+ }
+ else {
+ depth++;
+ if(aNode == myRoot)
+ retNodes = new TreeNode[depth];
+ else
+ retNodes = getPathToRoot(aNode.getParent(), depth);
+ retNodes[retNodes.length - depth] = aNode;
+ }
+ return retNodes;
+ }
+
+ public void removeNodeFromParent(TreeBuilderNode node) {
+ final TreeBuilderNode parent = (TreeBuilderNode)node.getParent();
+ if (parent != null) {
+ parent.remove(node);
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/tree/TreeBuilderNode.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/tree/TreeBuilderNode.java
new file mode 100644
index 0000000..f67946f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/tree/TreeBuilderNode.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.tree;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import java.util.Enumeration;
+
+/**
+ * User: lex
+ * Date: Sep 10, 2003
+ * Time: 7:01:02 PM
+ */
+public abstract class TreeBuilderNode extends DefaultMutableTreeNode{
+ private boolean myChildrenBuilt = false;
+
+ public TreeBuilderNode(Object userObject) {
+ super(userObject);
+ }
+
+ abstract protected TreeBuilder getTreeBuilder();
+
+ private void checkChildren() {
+ synchronized (this) {
+ if (myChildrenBuilt) {
+ return;
+ }
+ myChildrenBuilt = true;
+ }
+ final TreeBuilder treeBuilder = getTreeBuilder();
+ if(treeBuilder.isExpandable(this)) {
+ treeBuilder.buildChildren(this);
+ }
+ }
+
+ public void clear() {
+ synchronized (this) {
+ myChildrenBuilt = false;
+ }
+ }
+
+ //TreeNode interface
+ public int getChildCount() {
+ checkChildren();
+ return super.getChildCount();
+ }
+
+ public boolean getAllowsChildren() {
+ checkChildren();
+ return super.getAllowsChildren();
+ }
+
+ public boolean isLeaf() {
+ return !getTreeBuilder().isExpandable(this);
+ }
+
+ public Enumeration children() {
+ checkChildren();
+ return super.children();
+ }
+
+ public Enumeration rawChildren() {
+ return super.children();
+ }
+
+ public TreeNode getChildAt(int childIndex) {
+ checkChildren();
+ return super.getChildAt(childIndex);
+ }
+
+ public int getIndex(TreeNode node) {
+ checkChildren();
+ return super.getIndex(node);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java
new file mode 100644
index 0000000..3b87a5f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.PrimitiveValue;
+import com.sun.jdi.Value;
+
+public class ArgumentValueDescriptorImpl extends ValueDescriptorImpl{
+ private final int myIndex;
+ private final Value myValue;
+ private String myName;
+ private boolean myParameterNameCalcutated;
+
+ public ArgumentValueDescriptorImpl(Project project, int index, Value value) {
+ super(project);
+ myIndex = index;
+ myValue = value;
+ myName = "arg" + String.valueOf(index);
+ setLvalue(true);
+ }
+
+ public boolean isPrimitive() {
+ return myValue instanceof PrimitiveValue;
+ }
+
+ public Value calcValue(final EvaluationContextImpl evaluationContext) throws EvaluateException {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final SourcePosition position = ContextUtil.getSourcePosition(evaluationContext);
+ if (position != null) {
+ final PsiMethod method = PsiTreeUtil.getParentOfType(position.getElementAt(), PsiMethod.class);
+ if (method != null) {
+ final PsiParameterList params = method.getParameterList();
+ if (myIndex < params.getParametersCount()) {
+ final PsiParameter param = params.getParameters()[myIndex];
+ myName = param.getName();
+ myParameterNameCalcutated = true;
+ }
+ }
+ }
+ }
+ });
+ return myValue;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public String calcValueName() {
+ return getName();
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ if (!myParameterNameCalcutated) {
+ return null;
+ }
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
+ try {
+ return elementFactory.createExpressionFromText(getName(), PositionUtil.getContextElement(context));
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(DebuggerBundle.message("error.invalid.local.variable.name", getName()), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArrayElementDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArrayElementDescriptorImpl.java
new file mode 100644
index 0000000..93982cc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArrayElementDescriptorImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.ui.tree.ArrayElementDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.ArrayReference;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.Value;
+
+public class ArrayElementDescriptorImpl extends ValueDescriptorImpl implements ArrayElementDescriptor{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl");
+
+ private final int myIndex;
+ private final ArrayReference myArray;
+
+ public ArrayElementDescriptorImpl(Project project, ArrayReference array, int index) {
+ super(project);
+ myArray = array;
+ myIndex = index;
+ setLvalue(true);
+ }
+
+ public int getIndex() {
+ return myIndex;
+ }
+
+ public ArrayReference getArray() {
+ return myArray;
+ }
+
+ public String getName() {
+ return String.valueOf(myIndex);
+ }
+
+ public String calcValueName() {
+ return "[" + getName() + "]";
+ }
+
+ public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
+ try {
+ return myArray.getValue(myIndex);
+ }
+ catch (ObjectCollectedException e) {
+ throw EvaluateExceptionUtil.ARRAY_WAS_COLLECTED;
+ }
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
+ try {
+ return elementFactory.createExpressionFromText("this[" + myIndex + "]", null);
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java
new file mode 100644
index 0000000..e80bf60
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java
@@ -0,0 +1,691 @@
+/*
+ * 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 DebuggerTree
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.settings.ThreadsViewSettings;
+import com.intellij.debugger.ui.impl.DebuggerTreeBase;
+import com.intellij.debugger.ui.impl.tree.TreeBuilder;
+import com.intellij.debugger.ui.impl.tree.TreeBuilderNode;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.render.ChildrenBuilder;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.openapi.actionSystem.DataKey;
+import com.intellij.openapi.actionSystem.DataProvider;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.SpeedSearchComparator;
+import com.intellij.ui.TreeSpeedSearch;
+import com.sun.jdi.*;
+
+import javax.swing.*;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.util.*;
+import java.util.List;
+
+public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvider {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.DebuggerTree");
+ protected static final Key<Rectangle> VISIBLE_RECT = Key.create("VISIBLE_RECT");
+
+ public static final DataKey<DebuggerTree> DATA_KEY = DataKey.create("DebuggerTree");
+
+ protected final NodeManagerImpl myNodeManager;
+
+ private DebuggerContextImpl myDebuggerContext = DebuggerContextImpl.EMPTY_CONTEXT;
+
+ private DebuggerTreeNodeImpl myEditedNode;
+
+ public DebuggerTree(Project project) {
+ super(null, project);
+ setScrollsOnExpand(false);
+ myNodeManager = createNodeManager(project);
+
+ final TreeBuilder model = new TreeBuilder(this) {
+ public void buildChildren(TreeBuilderNode node) {
+ final DebuggerTreeNodeImpl debuggerTreeNode = (DebuggerTreeNodeImpl)node;
+ if (debuggerTreeNode.getDescriptor() instanceof DefaultNodeDescriptor) {
+ return;
+ }
+ buildNode(debuggerTreeNode);
+ }
+
+ public boolean isExpandable(TreeBuilderNode builderNode) {
+ return DebuggerTree.this.isExpandable((DebuggerTreeNodeImpl)builderNode);
+ }
+ };
+ model.setRoot(getNodeFactory().getDefaultNode());
+ model.addTreeModelListener(new TreeModelListener() {
+ public void treeNodesChanged(TreeModelEvent event) {
+ hideTooltip();
+ }
+
+ public void treeNodesInserted(TreeModelEvent event) {
+ hideTooltip();
+ }
+
+ public void treeNodesRemoved(TreeModelEvent event) {
+ hideTooltip();
+ }
+
+ public void treeStructureChanged(TreeModelEvent event) {
+ hideTooltip();
+ }
+ });
+
+ setModel(model);
+
+ final TreeSpeedSearch search = new TreeSpeedSearch(this);
+ search.setComparator(new SpeedSearchComparator(false));
+ }
+
+ protected NodeManagerImpl createNodeManager(Project project) {
+ return new NodeManagerImpl(project, this);
+ }
+
+ public void dispose() {
+ myNodeManager.dispose();
+ myDebuggerContext = DebuggerContextImpl.EMPTY_CONTEXT;
+ super.dispose();
+ }
+
+ protected boolean isExpandable(DebuggerTreeNodeImpl node) {
+ NodeDescriptorImpl descriptor = node.getDescriptor();
+ return descriptor.isExpandable();
+ }
+
+ public Object getData(String dataId) {
+ if (DebuggerTree.DATA_KEY.is(dataId)) {
+ return this;
+ }
+ return null;
+ }
+
+
+ private void buildNode(final DebuggerTreeNodeImpl node) {
+ if (node == null || node.getDescriptor() == null) {
+ return;
+ }
+ final DebugProcessImpl debugProcess = getDebuggerContext().getDebugProcess();
+ if (debugProcess != null) {
+ DebuggerCommandImpl command = getBuildNodeCommand(node);
+ if (command != null) {
+ node.add(myNodeManager.createMessageNode(MessageDescriptor.EVALUATING));
+ debugProcess.getManagerThread().schedule(command);
+ }
+ }
+ }
+
+ // todo: convert "if" into instance method call
+ protected DebuggerCommandImpl getBuildNodeCommand(final DebuggerTreeNodeImpl node) {
+ if (node.getDescriptor() instanceof StackFrameDescriptorImpl) {
+ return new BuildStackFrameCommand(node);
+ }
+ else if (node.getDescriptor() instanceof ValueDescriptorImpl) {
+ return new BuildValueNodeCommand(node);
+ }
+ else if (node.getDescriptor() instanceof StaticDescriptorImpl) {
+ return new BuildStaticNodeCommand(node);
+ }
+ else if (node.getDescriptor() instanceof ThreadDescriptorImpl) {
+ return new BuildThreadCommand(node);
+ }
+ else if (node.getDescriptor() instanceof ThreadGroupDescriptorImpl) {
+ return new BuildThreadGroupCommand(node);
+ }
+ LOG.assertTrue(false);
+ return null;
+ }
+
+ public void saveState(DebuggerTreeNodeImpl node) {
+ if (node.getDescriptor() != null) {
+ TreePath path = new TreePath(node.getPath());
+ node.getDescriptor().myIsExpanded = isExpanded(path);
+ node.getDescriptor().myIsSelected = getSelectionModel().isPathSelected(path);
+ Rectangle rowBounds = getRowBounds(getRowForPath(path));
+ if (rowBounds != null && getVisibleRect().contains(rowBounds)) {
+ node.getDescriptor().putUserData(VISIBLE_RECT, getVisibleRect());
+ node.getDescriptor().myIsVisible = true;
+ }
+ else {
+ node.getDescriptor().putUserData(VISIBLE_RECT, null);
+ node.getDescriptor().myIsVisible = false;
+ }
+ }
+
+ for (Enumeration e = node.rawChildren(); e.hasMoreElements();) {
+ DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
+ saveState(child);
+ }
+ }
+
+ public void restoreState(DebuggerTreeNodeImpl node) {
+ restoreStateImpl(node);
+ scrollToVisible(node);
+ }
+
+ protected final void scrollToVisible(DebuggerTreeNodeImpl scopeNode) {
+ final TreePath rootPath = new TreePath(scopeNode.getPath());
+ final int rowCount = getRowCount();
+ for (int idx = rowCount - 1; idx >= 0; idx--) {
+ final TreePath treePath = getPathForRow(idx);
+ if (treePath != null) {
+ if (!rootPath.isDescendant(treePath)) {
+ continue;
+ }
+ final DebuggerTreeNodeImpl pathNode = (DebuggerTreeNodeImpl)treePath.getLastPathComponent();
+ final NodeDescriptorImpl descriptor = pathNode.getDescriptor();
+
+ if (descriptor != null && descriptor.myIsVisible) {
+ final Rectangle visibleRect = descriptor.getUserData(VISIBLE_RECT);
+ if (visibleRect != null) {
+ // prefer visible rect
+ scrollRectToVisible(visibleRect);
+ }
+ else {
+ scrollPathToVisible(treePath);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public void scrollRectToVisible(Rectangle aRect) {
+ // see IDEADEV-432
+ aRect.width += aRect.x;
+ aRect.x = 0;
+ super.scrollRectToVisible(aRect);
+ }
+
+ private void restoreStateImpl(DebuggerTreeNodeImpl node) {
+ restoreNodeState(node);
+ if (node.getDescriptor().myIsExpanded) {
+ for (Enumeration e = node.rawChildren(); e.hasMoreElements();) {
+ DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
+ restoreStateImpl(child);
+ }
+ }
+ }
+
+ public void restoreState() {
+ clearSelection();
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
+ if (root != null) {
+ restoreState(root);
+ }
+ }
+
+ protected void restoreNodeState(DebuggerTreeNodeImpl node) {
+ final NodeDescriptorImpl descriptor = node.getDescriptor();
+ if (descriptor != null) {
+ if (node.getParent() == null) {
+ descriptor.myIsExpanded = true;
+ }
+
+ TreePath path = new TreePath(node.getPath());
+ if (descriptor.myIsExpanded) {
+ expandPath(path);
+ }
+ if (descriptor.myIsSelected) {
+ addSelectionPath(path);
+ }
+ }
+ }
+
+ public NodeManagerImpl getNodeFactory() {
+ return myNodeManager;
+ }
+
+ public TreeBuilder getMutableModel() {
+ return (TreeBuilder)getModel();
+ }
+
+ public void removeAllChildren() {
+ DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
+ root.removeAllChildren();
+ treeChanged();
+ }
+
+ public void showMessage(MessageDescriptor messageDesc) {
+ DebuggerTreeNodeImpl root = getNodeFactory().getDefaultNode();
+ getMutableModel().setRoot(root);
+ DebuggerTreeNodeImpl message = root.add(messageDesc);
+ treeChanged();
+ expandPath(new TreePath(message.getPath()));
+ }
+
+ public void showMessage(String messageText) {
+ showMessage(new MessageDescriptor(messageText));
+ }
+
+ public final void treeChanged() {
+ DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)getModel().getRoot();
+ if (node != null) {
+ getMutableModel().nodeStructureChanged(node);
+ restoreState();
+ }
+ }
+
+ protected abstract void build(DebuggerContextImpl context);
+
+ protected final void buildWhenPaused(DebuggerContextImpl context, RefreshDebuggerTreeCommand command) {
+ DebuggerSession debuggerSession = context.getDebuggerSession();
+
+ if (ApplicationManager.getApplication().isUnitTestMode() || debuggerSession.getState() == DebuggerSession.STATE_PAUSED) {
+ showMessage(MessageDescriptor.EVALUATING);
+ context.getDebugProcess().getManagerThread().schedule(command);
+ }
+ else {
+ showMessage(context.getDebuggerSession().getStateDescription());
+ }
+ }
+
+ public void rebuild(final DebuggerContextImpl context) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ final DebugProcessImpl process = context.getDebugProcess();
+ if (process == null) {
+ return; // empty context, no process available yet
+ }
+ myDebuggerContext = context;
+ saveState();
+ process.getManagerThread().schedule(new DebuggerCommandImpl() {
+ protected void action() throws Exception {
+ getNodeFactory().setHistoryByContext(context);
+ }
+ public Priority getPriority() {
+ return Priority.NORMAL;
+ }
+ });
+
+ build(context);
+ }
+
+ public void saveState() {
+ saveState((DebuggerTreeNodeImpl)getModel().getRoot());
+ }
+
+ public void onEditorShown(DebuggerTreeNodeImpl node) {
+ myEditedNode = node;
+ hideTooltip();
+ }
+
+ public void onEditorHidden(DebuggerTreeNodeImpl node) {
+ if (myEditedNode != null) {
+ assert myEditedNode == node;
+ myEditedNode = null;
+ }
+ }
+
+ @Override
+ public JComponent createToolTip(MouseEvent e) {
+ return myEditedNode != null ? null : super.createToolTip(e);
+ }
+
+ protected abstract static class RefreshDebuggerTreeCommand extends SuspendContextCommandImpl {
+ private final DebuggerContextImpl myDebuggerContext;
+
+ public Priority getPriority() {
+ return Priority.NORMAL;
+ }
+
+ public RefreshDebuggerTreeCommand(DebuggerContextImpl context) {
+ super(context.getSuspendContext());
+ myDebuggerContext = context;
+ }
+
+ public final DebuggerContextImpl getDebuggerContext() {
+ return myDebuggerContext;
+ }
+ }
+
+ public DebuggerContextImpl getDebuggerContext() {
+ return myDebuggerContext;
+ }
+
+ public abstract class BuildNodeCommand extends DebuggerContextCommandImpl {
+ private final DebuggerTreeNodeImpl myNode;
+
+ protected final List<DebuggerTreeNode> myChildren = new LinkedList<DebuggerTreeNode>();
+
+ protected BuildNodeCommand(DebuggerTreeNodeImpl node) {
+ super(DebuggerTree.this.getDebuggerContext());
+ myNode = node;
+ }
+
+ public Priority getPriority() {
+ return Priority.NORMAL;
+ }
+
+ public DebuggerTreeNodeImpl getNode() {
+ return myNode;
+ }
+
+ protected void updateUI(final boolean scrollToVisible) {
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ myNode.removeAllChildren();
+ for (DebuggerTreeNode debuggerTreeNode : myChildren) {
+ myNode.add(debuggerTreeNode);
+ }
+ myNode.childrenChanged(scrollToVisible);
+ }
+ });
+ }
+ }
+
+ protected class BuildStackFrameCommand extends BuildNodeCommand {
+ public BuildStackFrameCommand(DebuggerTreeNodeImpl stackNode) {
+ super(stackNode);
+ }
+
+ public final void threadAction() {
+ try {
+ final StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)getNode().getDescriptor();
+ final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
+ if (frame == null) {
+ return;
+ }
+ final Location location = frame.location();
+
+ final ObjectReference thisObjectReference = frame.thisObject();
+
+ final DebuggerContextImpl debuggerContext = getDebuggerContext();
+ final EvaluationContextImpl evaluationContext = debuggerContext.createEvaluationContext();
+
+ if (!debuggerContext.isEvaluationPossible()) {
+ myChildren.add(myNodeManager.createNode(MessageDescriptor.EVALUATION_NOT_POSSIBLE, evaluationContext));
+ }
+
+ final NodeDescriptor descriptor;
+ if (thisObjectReference != null) {
+ descriptor = myNodeManager.getThisDescriptor(stackDescriptor, thisObjectReference);
+ }
+ else {
+ final ReferenceType type = location.method().declaringType();
+ descriptor = myNodeManager.getStaticDescriptor(stackDescriptor, type);
+ }
+ myChildren.add(myNodeManager.createNode(descriptor, evaluationContext));
+
+ final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
+ if (classRenderer.SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES) {
+ if (thisObjectReference != null && evaluationContext.getDebugProcess().getVirtualMachineProxy().canGetSyntheticAttribute()) {
+ final ReferenceType thisRefType = thisObjectReference.referenceType();
+ if (thisRefType instanceof ClassType && thisRefType.equals(location.declaringType()) && thisRefType.name().contains("$")) { // makes sense for nested classes only
+ final ClassType clsType = (ClassType)thisRefType;
+ for (Field field : clsType.fields()) {
+ if (field.isSynthetic() && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) {
+ final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(stackDescriptor, thisObjectReference, field);
+ myChildren.add(myNodeManager.createNode(fieldDescriptor, evaluationContext));
+ }
+ }
+ }
+ }
+ }
+
+ try {
+ buildVariables(stackDescriptor, evaluationContext);
+ if (classRenderer.SORT_ASCENDING) {
+ Collections.sort(myChildren, NodeManagerImpl.getNodeComparator());
+ }
+ }
+ catch (EvaluateException e) {
+ myChildren.add(myNodeManager.createMessageNode(new MessageDescriptor(e.getMessage())));
+ }
+ // add last method return value if any
+ final Pair<Method, Value> methodValuePair = debuggerContext.getDebugProcess().getLastExecutedMethod();
+ if (methodValuePair != null) {
+ final ValueDescriptorImpl returnValueDescriptor =
+ myNodeManager.getMethodReturnValueDescriptor(stackDescriptor, methodValuePair.getFirst(), methodValuePair.getSecond());
+ final DebuggerTreeNodeImpl methodReturnValueNode = myNodeManager.createNode(returnValueDescriptor, evaluationContext);
+ myChildren.add(1, methodReturnValueNode);
+ }
+ }
+ catch (EvaluateException e) {
+ myChildren.clear();
+ myChildren.add(myNodeManager.createMessageNode(new MessageDescriptor(e.getMessage())));
+ }
+ catch (InvalidStackFrameException e) {
+ LOG.info(e);
+ myChildren.clear();
+ notifyCancelled();
+ }
+ catch (InternalException e) {
+ if (e.errorCode() == 35) {
+ myChildren.add(
+ myNodeManager.createMessageNode(new MessageDescriptor(DebuggerBundle.message("error.corrupt.debug.info", e.getMessage()))));
+ }
+ else {
+ throw e;
+ }
+ }
+
+ updateUI(true);
+ }
+
+ protected void buildVariables(final StackFrameDescriptorImpl stackDescriptor, final EvaluationContextImpl evaluationContext) throws EvaluateException {
+ final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
+ if (frame != null) {
+ for (final LocalVariableProxyImpl local : frame.visibleVariables()) {
+ final LocalVariableDescriptorImpl localVariableDescriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, local);
+ final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(localVariableDescriptor, evaluationContext);
+ myChildren.add(variableNode);
+ }
+ }
+ }
+ }
+
+ private class BuildValueNodeCommand extends BuildNodeCommand implements ChildrenBuilder {
+ public BuildValueNodeCommand(DebuggerTreeNodeImpl node) {
+ super(node);
+ }
+
+ public void threadAction() {
+ ValueDescriptorImpl descriptor = (ValueDescriptorImpl)getNode().getDescriptor();
+ try {
+ final NodeRenderer renderer = descriptor.getRenderer(getSuspendContext().getDebugProcess());
+ renderer.buildChildren(descriptor.getValue(), this, getDebuggerContext().createEvaluationContext());
+ }
+ catch (ObjectCollectedException e) {
+ getNode().removeAllChildren();
+ getNode().add(getNodeFactory().createMessageNode(
+ new MessageDescriptor(DebuggerBundle.message("error.cannot.build.node.children.object.collected", e.getMessage()))));
+ getNode().childrenChanged(false);
+ }
+ }
+
+ public NodeManagerImpl getNodeManager() {
+
+ return myNodeManager;
+ }
+
+ public NodeManagerImpl getDescriptorManager() {
+ return myNodeManager;
+ }
+
+ public ValueDescriptorImpl getParentDescriptor() {
+ return (ValueDescriptorImpl)getNode().getDescriptor();
+ }
+
+ public void setChildren(final List<DebuggerTreeNode> children) {
+ myChildren.addAll(children);
+ updateUI(false);
+ }
+ }
+
+ private class BuildStaticNodeCommand extends BuildNodeCommand {
+ public BuildStaticNodeCommand(DebuggerTreeNodeImpl node) {
+ super(node);
+ }
+
+ public void threadAction() {
+ final StaticDescriptorImpl sd = (StaticDescriptorImpl)getNode().getDescriptor();
+ final ReferenceType refType = sd.getType();
+ List<Field> fields = refType.allFields();
+ for (Field field : fields) {
+ if (field.isStatic()) {
+ final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(sd, null, field);
+ final EvaluationContextImpl evaluationContext = getDebuggerContext().createEvaluationContext();
+ final DebuggerTreeNodeImpl node = myNodeManager.createNode(fieldDescriptor, evaluationContext);
+ myChildren.add(node);
+ }
+ }
+
+ updateUI(true);
+ }
+ }
+
+ private class BuildThreadCommand extends BuildNodeCommand {
+ public BuildThreadCommand(DebuggerTreeNodeImpl threadNode) {
+ super(threadNode);
+ }
+
+ public void threadAction() {
+ ThreadDescriptorImpl threadDescriptor = ((ThreadDescriptorImpl)getNode().getDescriptor());
+ ThreadReferenceProxyImpl threadProxy = threadDescriptor.getThreadReference();
+ if (!threadProxy.isCollected() && getDebuggerContext().getDebugProcess().getSuspendManager().isSuspended(threadProxy)) {
+ int status = threadProxy.status();
+ if (!(status == ThreadReference.THREAD_STATUS_UNKNOWN) &&
+ !(status == ThreadReference.THREAD_STATUS_NOT_STARTED) &&
+ !(status == ThreadReference.THREAD_STATUS_ZOMBIE)) {
+ try {
+ for (StackFrameProxyImpl stackFrame : threadProxy.frames()) {
+ //Method method = stackFrame.location().method();
+ //ToDo :check whether is synthetic if (shouldDisplay(method)) {
+ myChildren.add(myNodeManager.createNode(myNodeManager.getStackFrameDescriptor(threadDescriptor, stackFrame),
+ getDebuggerContext().createEvaluationContext()));
+ }
+ }
+ catch (EvaluateException e) {
+ myChildren.clear();
+ myChildren.add(myNodeManager.createMessageNode(e.getMessage()));
+ LOG.debug(e);
+ //LOG.assertTrue(false);
+ // if we pause during evaluation of this method the exception is thrown
+ // private static void longMethod(){
+ // try {
+ // Thread.sleep(100000);
+ // } catch (InterruptedException e) {
+ // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ // }
+ // }
+ }
+ }
+ }
+ updateUI(true);
+ }
+ }
+
+ private class BuildThreadGroupCommand extends DebuggerCommandImpl {
+ private final DebuggerTreeNodeImpl myNode;
+ protected final List<DebuggerTreeNode> myChildren = new LinkedList<DebuggerTreeNode>();
+
+ public BuildThreadGroupCommand(DebuggerTreeNodeImpl node) {
+ myNode = node;
+ }
+
+ protected void action() throws Exception {
+ ThreadGroupDescriptorImpl groupDescriptor = (ThreadGroupDescriptorImpl)myNode.getDescriptor();
+ ThreadGroupReferenceProxyImpl threadGroup = groupDescriptor.getThreadGroupReference();
+
+ List<ThreadReferenceProxyImpl> threads = new ArrayList<ThreadReferenceProxyImpl>(threadGroup.threads());
+ Collections.sort(threads, ThreadReferenceProxyImpl.ourComparator);
+
+ final DebuggerContextImpl debuggerContext = getDebuggerContext();
+ final SuspendContextImpl suspendContext = debuggerContext.getSuspendContext();
+ final EvaluationContextImpl evaluationContext = suspendContext != null? debuggerContext.createEvaluationContext() : null;
+
+ boolean showCurrent = ThreadsViewSettings.getInstance().SHOW_CURRENT_THREAD;
+
+ for (final ThreadGroupReferenceProxyImpl group : threadGroup.threadGroups()) {
+ if (group != null) {
+ DebuggerTreeNodeImpl threadNode =
+ myNodeManager.createNode(myNodeManager.getThreadGroupDescriptor(groupDescriptor, group), evaluationContext);
+
+ if (showCurrent && ((ThreadGroupDescriptorImpl)threadNode.getDescriptor()).isCurrent()) {
+ myChildren.add(0, threadNode);
+ }
+ else {
+ myChildren.add(threadNode);
+ }
+ }
+ }
+
+ ArrayList<DebuggerTreeNodeImpl> threadNodes = new ArrayList<DebuggerTreeNodeImpl>();
+
+ for (ThreadReferenceProxyImpl thread : threads) {
+ if (thread != null) {
+ final DebuggerTreeNodeImpl threadNode = myNodeManager.createNode(myNodeManager.getThreadDescriptor(groupDescriptor, thread), evaluationContext);
+ if (showCurrent && ((ThreadDescriptorImpl)threadNode.getDescriptor()).isCurrent()) {
+ threadNodes.add(0, threadNode);
+ }
+ else {
+ threadNodes.add(threadNode);
+ }
+ }
+ }
+
+ myChildren.addAll(threadNodes);
+
+ updateUI(true);
+ }
+
+ protected void updateUI(final boolean scrollToVisible) {
+ DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
+ public void run() {
+ myNode.removeAllChildren();
+ for (DebuggerTreeNode debuggerTreeNode : myChildren) {
+ myNode.add(debuggerTreeNode);
+ }
+ myNode.childrenChanged(scrollToVisible);
+ }
+ });
+ }
+ }
+
+ public void hideTooltip() {
+ myTipManager.hideTooltip();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeInplaceEditor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeInplaceEditor.java
new file mode 100644
index 0000000..26801cc
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeInplaceEditor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.ui.tree.TreeInplaceEditor;
+
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+
+public abstract class DebuggerTreeInplaceEditor extends TreeInplaceEditor {
+ private final DebuggerTreeNodeImpl myNode;
+
+ protected Project getProject() {
+ return myNode.getTree().getProject();
+ }
+
+ public DebuggerTreeInplaceEditor(DebuggerTreeNodeImpl node) {
+ myNode = node;
+ }
+
+ protected TreePath getNodePath() {
+ return new TreePath(myNode.getPath());
+ }
+
+ protected JTree getTree() {
+ return myNode.getTree();
+ }
+
+ @Override
+ protected void onShown() {
+ myNode.getTree().onEditorShown(myNode);
+ }
+
+ @Override
+ protected void onHidden() {
+ myNode.getTree().onEditorHidden(myNode);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeNodeExpression.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeNodeExpression.java
new file mode 100644
index 0000000..56e0208
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeNodeExpression.java
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.codeInsight.ChangeContextUtil;
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.codeinsight.RuntimeTypeEvaluator;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.Value;
+
+/**
+ * User: lex
+ * Date: Oct 29, 2003
+ * Time: 9:24:52 PM
+ */
+public class DebuggerTreeNodeExpression {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeExpression");
+
+// private static PsiExpression beautifyExpression(PsiExpression expression) throws IncorrectOperationException {
+// final PsiElementFactory elementFactory = expression.getManager().getElementFactory();
+// final PsiParenthesizedExpression utility = (PsiParenthesizedExpression)elementFactory.createExpressionFromText(
+// "(expr)", expression.getContext());
+// utility.getExpression().replace(expression);
+//
+// PsiRecursiveElementVisitor visitor = new PsiRecursiveElementVisitor() {
+// @Override public void visitTypeCastExpression(PsiTypeCastExpression expression) {
+// try {
+// super.visitTypeCastExpression(expression);
+//
+// PsiElement parent;
+// PsiElement toBeReplaced = expression;
+// for (parent = expression.getParent();
+// parent instanceof PsiParenthesizedExpression && parent != utility;
+// parent = parent.getParent()) {
+// toBeReplaced = parent;
+// }
+//
+// if (parent instanceof PsiReferenceExpression) {
+// PsiReferenceExpression reference = ((PsiReferenceExpression)parent);
+// //((TypeCast)).member
+// PsiElement oldResolved = reference.resolve();
+//
+// if (oldResolved != null) {
+// PsiReferenceExpression newReference = ((PsiReferenceExpression)reference.copy());
+// newReference.getQualifierExpression().replace(expression.getOperand());
+// PsiElement newResolved = newReference.resolve();
+//
+// if (oldResolved == newResolved) {
+// toBeReplaced.replace(expression.getOperand());
+// }
+// else if (newResolved instanceof PsiMethod && oldResolved instanceof PsiMethod) {
+// if (isSuperMethod((PsiMethod)newResolved, (PsiMethod)oldResolved)) {
+// toBeReplaced.replace(expression.getOperand());
+// }
+// }
+// }
+// }
+// else {
+// toBeReplaced.replace(expression.getOperand());
+// }
+// }
+// catch (IncorrectOperationException e) {
+// throw new IncorrectOperationRuntimeException(e);
+// }
+// }
+//
+// @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
+// expression.acceptChildren(this);
+//
+// try {
+// JavaResolveResult resolveResult = expression.advancedResolve(false);
+//
+// PsiElement oldResolved = resolveResult.getElement();
+//
+// if(oldResolved == null) return;
+//
+// PsiReferenceExpression newReference;
+// if (expression instanceof PsiMethodCallExpression) {
+// int length = expression.getQualifierExpression().getTextRange().getLength();
+// PsiMethodCallExpression methodCall = (PsiMethodCallExpression)elementFactory.createExpressionFromText(
+// expression.getText().substring(length), expression.getContext());
+// newReference = methodCall.getMethodExpression();
+// }
+// else {
+// newReference =
+// (PsiReferenceExpression)elementFactory.createExpressionFromText(expression.getReferenceName(),
+// expression.getContext());
+// }
+//
+// PsiElement newResolved = newReference.resolve();
+// if (oldResolved == newResolved) {
+// expression.replace(newReference);
+// }
+// }
+// catch (IncorrectOperationException e) {
+// LOG.debug(e);
+// }
+// }
+// };
+//
+// try {
+// utility.accept(visitor);
+// }
+// catch (IncorrectOperationRuntimeException e) {
+// throw e.getException();
+// }
+// return utility.getExpression();
+// }
+
+ private static boolean isSuperMethod(PsiMethod superMethod, PsiMethod overridingMethod) {
+ PsiMethod[] superMethods = overridingMethod.findSuperMethods();
+ for (int i = 0; i < superMethods.length; i++) {
+ if (superMethods[i] == superMethod) {
+ return true;
+ }
+ else if (isSuperMethod(superMethod, superMethods[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static PsiExpression substituteThis(PsiExpression expressionWithThis, PsiExpression howToEvaluateThis, Value howToEvaluateThisValue)
+ throws EvaluateException {
+ PsiExpression result = (PsiExpression)expressionWithThis.copy();
+
+ PsiClass thisClass = PsiTreeUtil.getContextOfType(result, PsiClass.class, true);
+
+ boolean castNeeded = true;
+
+ if (thisClass != null) {
+ PsiType type = howToEvaluateThis.getType();
+ if(type != null) {
+ if(type instanceof PsiClassType) {
+ PsiClass psiClass = ((PsiClassType) type).resolve();
+ if(psiClass != null && (psiClass == thisClass || psiClass.isInheritor(thisClass, true))) {
+ castNeeded = false;
+ }
+ }
+ else if(type instanceof PsiArrayType) {
+ LanguageLevel languageLevel = PsiUtil.getLanguageLevel(expressionWithThis);
+ if(thisClass == JavaPsiFacade.getInstance(expressionWithThis.getProject()).getElementFactory().getArrayClass(languageLevel)) {
+ castNeeded = false;
+ }
+ }
+ }
+ }
+
+ if (castNeeded) {
+ howToEvaluateThis = castToRuntimeType(howToEvaluateThis, howToEvaluateThisValue, howToEvaluateThis.getContext());
+ }
+
+ ChangeContextUtil.encodeContextInfo(result, false);
+ PsiExpression psiExpression;
+ try {
+ psiExpression = (PsiExpression) ChangeContextUtil.decodeContextInfo(result, thisClass, howToEvaluateThis);
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(
+ DebuggerBundle.message("evaluation.error.invalid.this.expression", result.getText(), howToEvaluateThis.getText()), null);
+ }
+
+ try {
+ return JavaPsiFacade.getInstance(howToEvaluateThis.getProject()).getElementFactory()
+ .createExpressionFromText(psiExpression.getText(), howToEvaluateThis.getContext());
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(e.getMessage(), e);
+ }
+ }
+
+ public static PsiExpression castToRuntimeType(PsiExpression expression, Value value, PsiElement contextElement) throws EvaluateException {
+ if (!(value instanceof ObjectReference)) {
+ return expression;
+ }
+
+ ReferenceType valueType = ((ObjectReference)value).referenceType();
+ if (valueType == null) {
+ return expression;
+ }
+
+ Project project = expression.getProject();
+
+ PsiClass type = RuntimeTypeEvaluator.getCastableRuntimeType(project, value);
+ if (type == null) {
+ return expression;
+ }
+
+ PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
+ try {
+ PsiParenthesizedExpression parenthExpression = (PsiParenthesizedExpression)elementFactory.createExpressionFromText(
+ "((" + type.getQualifiedName() + ")expression)", null);
+ ((PsiTypeCastExpression)parenthExpression.getExpression()).getOperand().replace(expression);
+ return parenthExpression;
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(DebuggerBundle.message("error.invalid.type.name", type.getQualifiedName()), e);
+ }
+ }
+
+ /**
+ * @param qualifiedName the class qualified name to be resolved against the current execution context
+ * @return short name if the class could be resolved using short name,
+ * otherwise returns qualifiedName
+ */
+ public static String normalize(final String qualifiedName, PsiElement contextElement, Project project) {
+ if (contextElement == null) {
+ return qualifiedName;
+ }
+
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
+ PsiClass aClass = facade.findClass(qualifiedName, GlobalSearchScope.allScope(project));
+ if (aClass != null) {
+ return normalizePsiClass(aClass, contextElement, facade.getResolveHelper());
+ }
+ return qualifiedName;
+ }
+
+ private static String normalizePsiClass(PsiClass psiClass, PsiElement contextElement, PsiResolveHelper helper) {
+ String name = psiClass.getName();
+ PsiClass aClass = helper.resolveReferencedClass(name, contextElement);
+ if (psiClass.equals(aClass)) {
+ return name;
+ }
+ PsiClass parentClass = psiClass.getContainingClass();
+ if (parentClass != null) {
+ return normalizePsiClass(parentClass, contextElement, helper) + "." + name;
+ }
+ return psiClass.getQualifiedName();
+ }
+
+ public static PsiExpression getEvaluationExpression(DebuggerTreeNodeImpl node, DebuggerContextImpl context) throws EvaluateException {
+ if(node.getDescriptor() instanceof ValueDescriptorImpl) {
+ return ((ValueDescriptorImpl)node.getDescriptor()).getTreeEvaluation(node, context);
+ }
+ else {
+ LOG.error(node.getDescriptor() != null ? node.getDescriptor().getClass().getName() : "null");
+ return null;
+ }
+ }
+
+ public static TextWithImports createEvaluationText(final DebuggerTreeNodeImpl node, final DebuggerContextImpl context) throws EvaluateException {
+ final EvaluateException[] ex = new EvaluateException[] {null};
+ final TextWithImports textWithImports = PsiDocumentManager.getInstance(context.getProject()).commitAndRunReadAction(new Computable<TextWithImports>() {
+ public TextWithImports compute() {
+ try {
+ final PsiExpression expressionText = getEvaluationExpression(node, context);
+ if (expressionText != null) {
+ return new TextWithImportsImpl(expressionText);
+ }
+ }
+ catch (EvaluateException e) {
+ ex[0] = e;
+ }
+ return null;
+ }
+ });
+ if (ex[0] != null) {
+ throw ex[0];
+ }
+ return textWithImports;
+ }
+
+ private static class IncorrectOperationRuntimeException extends RuntimeException {
+ private final IncorrectOperationException myException;
+
+ public IncorrectOperationRuntimeException(IncorrectOperationException exception) {
+ myException = exception;
+ }
+
+ public IncorrectOperationException getException() { return myException; }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeNodeImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeNodeImpl.java
new file mode 100644
index 0000000..665a6a1
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTreeNodeImpl.java
@@ -0,0 +1,266 @@
+/*
+ * 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 DebuggerTreeNodeImpl
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.ui.impl.DebuggerTreeRenderer;
+import com.intellij.debugger.ui.impl.tree.TreeBuilder;
+import com.intellij.debugger.ui.impl.tree.TreeBuilderNode;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.ui.SimpleColoredText;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Map;
+
+public class DebuggerTreeNodeImpl extends TreeBuilderNode implements DebuggerTreeNode{
+ private Icon myIcon;
+ private SimpleColoredText myText;
+ private String myMarkupTooltipText;
+ private final DebuggerTree myTree;
+ private final Map myProperties = new HashMap();
+
+ private DebuggerTreeNodeImpl(DebuggerTree tree, NodeDescriptor descriptor) {
+ super(descriptor);
+ myTree = tree;
+ }
+
+ public DebuggerTreeNodeImpl getParent() {
+ return (DebuggerTreeNodeImpl) super.getParent();
+ }
+
+ protected TreeBuilder getTreeBuilder() {
+ return myTree.getMutableModel();
+ }
+
+ public DebuggerTree getTree() {
+ return myTree;
+ }
+
+ public String toString() {
+ return myText != null? myText.toString() : "";
+ }
+
+ public NodeDescriptorImpl getDescriptor() {
+ return (NodeDescriptorImpl)getUserObject();
+ }
+
+ public Project getProject() {
+ return getTree().getProject();
+ }
+
+ public void setRenderer(NodeRenderer renderer) {
+ ((ValueDescriptorImpl) getDescriptor()).setRenderer(renderer);
+ calcRepresentation();
+ }
+
+ private void updateCaches() {
+ final NodeDescriptorImpl descriptor = getDescriptor();
+ myIcon = DebuggerTreeRenderer.getDescriptorIcon(descriptor);
+ final DebuggerContextImpl context = getTree().getDebuggerContext();
+ myText = DebuggerTreeRenderer.getDescriptorText(context, descriptor, false);
+ if (descriptor instanceof ValueDescriptor) {
+ final ValueMarkup markup = ((ValueDescriptor)descriptor).getMarkup(context.getDebugProcess());
+ myMarkupTooltipText = markup != null? markup.getToolTipText() : null;
+ }
+ else {
+ myMarkupTooltipText = null;
+ }
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public SimpleColoredText getText() {
+ return myText;
+ }
+
+ @Nullable
+ public String getMarkupTooltipText() {
+ return myMarkupTooltipText;
+ }
+
+ public void clear() {
+ removeAllChildren();
+ myIcon = null;
+ myText = null;
+ super.clear();
+ }
+
+ private void update(final DebuggerContextImpl context, final Runnable runnable, boolean labelOnly) {
+ if(!labelOnly) {
+ clear();
+ }
+
+ if(context != null && context.getDebugProcess() != null) {
+ getTree().saveState(this);
+
+ myIcon = DebuggerTreeRenderer.getDescriptorIcon(MessageDescriptor.EVALUATING);
+ myText = DebuggerTreeRenderer.getDescriptorText(context, MessageDescriptor.EVALUATING, false);
+
+ context.getDebugProcess().getManagerThread().invoke(new DebuggerContextCommandImpl(context) {
+ public void threadAction() {
+ runnable.run();
+ }
+
+ protected void commandCancelled() {
+ clear();
+ getDescriptor().clear();
+ updateCaches();
+
+ labelChanged();
+ childrenChanged(true);
+ }
+ public Priority getPriority() {
+ return Priority.NORMAL;
+ }
+
+ });
+ }
+
+ labelChanged();
+ if(!labelOnly) {
+ childrenChanged(true);
+ }
+ }
+
+ public void calcLabel() {
+ final DebuggerContextImpl context = getTree().getDebuggerContext();
+ update(context, new Runnable() {
+ public void run() {
+ getDescriptor().updateRepresentation(context.createEvaluationContext(), new DescriptorLabelListener() {
+ public void labelChanged() {
+ updateCaches();
+ DebuggerTreeNodeImpl.this.labelChanged();
+ }
+ });
+ }
+ }, true);
+ }
+
+ public void calcRepresentation() {
+ final DebuggerContextImpl context = getTree().getDebuggerContext();
+ update(context, new Runnable() {
+ public void run() {
+ getDescriptor().updateRepresentation(context.createEvaluationContext(), new DescriptorLabelListener() {
+ public void labelChanged() {
+ updateCaches();
+ DebuggerTreeNodeImpl.this.labelChanged();
+ }
+ });
+ }
+ }, false);
+ }
+
+ public void calcValue() {
+ final DebuggerContextImpl context = getTree().getDebuggerContext();
+ update(
+ context,
+ new Runnable() {
+ public void run() {
+ EvaluationContextImpl evaluationContext = context.createEvaluationContext();
+ getDescriptor().setContext(evaluationContext);
+ getDescriptor().updateRepresentation(evaluationContext, new DescriptorLabelListener() {
+ public void labelChanged() {
+ updateCaches();
+ DebuggerTreeNodeImpl.this.labelChanged();
+ }
+ });
+ DebuggerTreeNodeImpl.this.childrenChanged(true);
+ }
+ }, false);
+ }
+
+ private void invoke(Runnable r) {
+ if(ApplicationManager.getApplication().isDispatchThread()) {
+ r.run();
+ }
+ else {
+ SwingUtilities.invokeLater(r);
+ }
+ }
+
+ public void labelChanged() {
+ invoke(new Runnable() {
+ public void run() {
+ updateCaches();
+ getTree().getMutableModel().nodeChanged(DebuggerTreeNodeImpl.this);
+ }
+ });
+ }
+
+ public void childrenChanged(final boolean scrollToVisible) {
+ invoke(new Runnable() {
+ public void run() {
+ getTree().getMutableModel().nodeStructureChanged(DebuggerTreeNodeImpl.this);
+ getTree().restoreState(DebuggerTreeNodeImpl.this);
+ }
+ });
+ }
+
+ public DebuggerTreeNodeImpl add(MessageDescriptor message) {
+ DebuggerTreeNodeImpl node = getNodeFactory().createMessageNode(message);
+ add(node);
+ return node;
+ }
+
+ public NodeManagerImpl getNodeFactory() {
+ return myTree.getNodeFactory();
+ }
+
+ public Object getProperty(Key key) {
+ return myProperties.get(key);
+ }
+
+ public void putProperty(Key key, Object data) {
+ myProperties.put(key, data);
+ }
+
+ public static DebuggerTreeNodeImpl createNodeNoUpdate(DebuggerTree tree, NodeDescriptor descriptor) {
+ DebuggerTreeNodeImpl node = new DebuggerTreeNodeImpl(tree, descriptor);
+ node.updateCaches();
+ return node;
+ }
+
+ protected static DebuggerTreeNodeImpl createNode(DebuggerTree tree, NodeDescriptorImpl descriptor, EvaluationContextImpl evaluationContext) {
+ final DebuggerTreeNodeImpl node = new DebuggerTreeNodeImpl(tree, descriptor);
+ descriptor.updateRepresentationNoNotify(evaluationContext, new DescriptorLabelListener() {
+ public void labelChanged() {
+ node.updateCaches();
+ node.labelChanged();
+ }
+ });
+ node.updateCaches();
+ return node;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DefaultNodeDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DefaultNodeDescriptor.java
new file mode 100644
index 0000000..ba86515
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DefaultNodeDescriptor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.openapi.diagnostic.Logger;
+
+
+public final class DefaultNodeDescriptor extends NodeDescriptorImpl{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.DefaultNodeDescriptor");
+ public boolean equals(Object obj) {
+ return obj instanceof DefaultNodeDescriptor;
+ }
+
+ public int hashCode() {
+ return 0;
+ }
+
+ public boolean isExpandable() {
+ return true;
+ }
+
+ public void setContext(EvaluationContextImpl context) {
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener) {
+ LOG.assertTrue(false);
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DescriptorTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DescriptorTree.java
new file mode 100644
index 0000000..5dd5acb
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DescriptorTree.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+public class DescriptorTree {
+ private final HashMap<NodeDescriptor, List<NodeDescriptor>> myChildrenMap = new HashMap<NodeDescriptor, List<NodeDescriptor>>();
+ private final List<NodeDescriptor> myRootChildren = new ArrayList<NodeDescriptor>();
+ private final boolean myInitial;
+ private int myFrameCount = -1;
+ private int myFrameIndex = -1;
+
+ public DescriptorTree() {
+ this(false);
+ }
+
+ public DescriptorTree(boolean isInitial) {
+ myInitial = isInitial;
+ }
+
+ public void clear() {
+ myChildrenMap.clear();
+ myRootChildren.clear();
+ }
+
+ public boolean frameIdEquals(final int frameCount, final int frameIndex) {
+ return myFrameCount == frameCount && myFrameIndex == frameIndex;
+ }
+
+ public void setFrameId(final int frameCount, final int frameIndex) {
+ myFrameIndex = frameIndex;
+ myFrameCount = frameCount;
+ }
+
+ public void addChild(NodeDescriptor parent, NodeDescriptor child) {
+ List<NodeDescriptor> children;
+
+ if(parent == null) {
+ children = myRootChildren;
+ }
+ else {
+ children = myChildrenMap.get(parent);
+ if(children == null) {
+ children = new ArrayList<NodeDescriptor>();
+ myChildrenMap.put(parent, children);
+ }
+ }
+ children.add(child);
+ if (myInitial && child instanceof LocalVariableDescriptorImpl) {
+ ((LocalVariableDescriptorImpl)child).setNewLocal(false);
+ }
+ }
+
+ public List<NodeDescriptor> getChildren(NodeDescriptor parent) {
+ if(parent == null) {
+ return myRootChildren;
+ }
+
+ List<NodeDescriptor> children = myChildrenMap.get(parent);
+ return children != null ? children : Collections.<NodeDescriptor>emptyList();
+ }
+
+ public void dfst(DFSTWalker walker) {
+ dfstImpl(null, myRootChildren, walker);
+ }
+
+ private void dfstImpl(NodeDescriptor descriptor, List<NodeDescriptor> children, DFSTWalker walker) {
+ if(children != null) {
+ for (NodeDescriptor child : children) {
+ walker.visit(descriptor, child);
+ dfstImpl(child, myChildrenMap.get(child), walker);
+ }
+ }
+ }
+
+ public interface DFSTWalker {
+ void visit(NodeDescriptor parent, NodeDescriptor child);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java
new file mode 100644
index 0000000..e09c17c
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2000-2011 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.DebuggerInvocationUtil;
+import com.intellij.debugger.EvaluatingComputable;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.engine.evaluation.expression.Modifier;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiCodeFragment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionCodeFragment;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author lex
+ */
+public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
+ private Modifier myModifier;
+ protected TextWithImports myText;
+ private CodeFragmentFactory myCodeFragmentFactory = null; // used to force specific context, e.g. from evaluation
+
+ protected EvaluationDescriptor(TextWithImports text, Project project, Value value) {
+ super(project, value);
+ myText = text;
+ }
+
+ protected EvaluationDescriptor(TextWithImports text, Project project) {
+ super(project);
+ setLvalue(false);
+ myText = text;
+ }
+
+ public final void setCodeFragmentFactory(CodeFragmentFactory codeFragmentFactory) {
+ myCodeFragmentFactory = codeFragmentFactory != null? new CodeFragmentFactoryContextWrapper(codeFragmentFactory) : null;
+ }
+
+ @Nullable
+ public final CodeFragmentFactory getCodeFragmentFactory() {
+ return myCodeFragmentFactory;
+ }
+
+ protected final @NotNull CodeFragmentFactory getEffectiveCodeFragmentFactory(final PsiElement psiContext) {
+ if (myCodeFragmentFactory != null) {
+ return myCodeFragmentFactory;
+ }
+ return DebuggerUtilsEx.getEffectiveCodeFragmentFactory(psiContext);
+ }
+
+ protected abstract EvaluationContextImpl getEvaluationContext (EvaluationContextImpl evaluationContext);
+
+ protected abstract PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException;
+
+ public final Value calcValue(final EvaluationContextImpl evaluationContext) throws EvaluateException {
+ try {
+ final EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext);
+
+ final ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
+ public ExpressionEvaluator compute() throws EvaluateException {
+ final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext);
+ return getEffectiveCodeFragmentFactory(psiContext).getEvaluatorBuilder().build(getEvaluationCode(thisEvaluationContext),
+ ContextUtil.getSourcePosition(thisEvaluationContext));
+ }
+ });
+
+
+ if (!thisEvaluationContext.getDebugProcess().isAttached()) {
+ throw EvaluateExceptionUtil.PROCESS_EXITED;
+ }
+ StackFrameProxyImpl frameProxy = thisEvaluationContext.getFrameProxy();
+ if (frameProxy == null) {
+ throw EvaluateExceptionUtil.NULL_STACK_FRAME;
+ }
+
+ final Value value = evaluator.evaluate(thisEvaluationContext);
+ if (value instanceof ObjectReference) {
+ thisEvaluationContext.getSuspendContext().keep(((ObjectReference)value));
+ }
+ myModifier = evaluator.getModifier();
+ setLvalue(myModifier != null);
+
+ return value;
+ }
+ catch (final EvaluateException ex) {
+ throw new EvaluateException(ex.getLocalizedMessage(), ex);
+ }
+ }
+
+ public String calcValueName() {
+ return getName();
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ PsiElement evaluationCode = getEvaluationCode(context);
+ if(evaluationCode instanceof PsiExpressionCodeFragment) {
+ return ((PsiExpressionCodeFragment)evaluationCode).getExpression();
+ }
+ else {
+ throw new EvaluateException(DebuggerBundle.message("error.cannot.create.expression.from.code.fragment"), null);
+ }
+ }
+
+ public Modifier getModifier() {
+ return myModifier;
+ }
+
+ public boolean canSetValue() {
+ return super.canSetValue() && myModifier != null && myModifier.canSetValue();
+ }
+
+ public TextWithImports getEvaluationText() {
+ return myText;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java
new file mode 100644
index 0000000..97e1c45
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.tree.FieldDescriptor;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NotNull;
+
+public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDescriptor{
+ public static final String OUTER_LOCAL_VAR_FIELD_PREFIX = "val$";
+ private final Field myField;
+ private final ObjectReference myObject;
+ private Boolean myIsPrimitive = null;
+ private final boolean myIsStatic;
+
+ public FieldDescriptorImpl(Project project, ObjectReference objRef, @NotNull Field field) {
+ super(project);
+ myObject = objRef;
+ myField = field;
+ myIsStatic = field.isStatic();
+ setLvalue(!field.isFinal());
+ }
+
+ public Field getField() {
+ return myField;
+ }
+
+ public ObjectReference getObject() {
+ return myObject;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context) {
+ if (context.getFrameProxy() == null) return null;
+ final ReferenceType type = myField.declaringType();
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
+ final String fieldName = myField.name();
+ if (fieldName.startsWith(OUTER_LOCAL_VAR_FIELD_PREFIX)) {
+ // this field actually mirrors a local variable in the outer class
+ String varName = fieldName.substring(fieldName.lastIndexOf('$') + 1);
+ PsiElement element = PositionUtil.getContextElement(context);
+ if (element == null) {
+ return null;
+ }
+ PsiClass aClass = PsiTreeUtil.getParentOfType(element, PsiClass.class, false);
+ if (aClass == null) {
+ return null;
+ }
+ aClass = (PsiClass) aClass.getNavigationElement();
+ PsiVariable psiVariable = facade.getResolveHelper().resolveReferencedVariable(varName, aClass);
+ if (psiVariable == null) {
+ return null;
+ }
+ return SourcePosition.createFromOffset(psiVariable.getContainingFile(), psiVariable.getTextOffset());
+ }
+ else {
+ PsiClass aClass =
+ facade.findClass(type.name().replace('$', '.'), GlobalSearchScope.allScope(myProject));
+ if (aClass == null) return null;
+ aClass = (PsiClass) aClass.getNavigationElement();
+ PsiField[] fields = aClass.getFields();
+ for (PsiField field : fields) {
+ if (fieldName.equals(field.getName())) {
+ return SourcePosition.createFromOffset(field.getContainingFile(), field.getTextOffset());
+ }
+ }
+ return null;
+ }
+ }
+
+ public void setAncestor(NodeDescriptor oldDescriptor) {
+ super.setAncestor(oldDescriptor);
+ final Boolean isPrimitive = ((FieldDescriptorImpl)oldDescriptor).myIsPrimitive;
+ if (isPrimitive != null) { // was cached
+ // do not loose cached info
+ myIsPrimitive = isPrimitive;
+ }
+ }
+
+
+ public boolean isPrimitive() {
+ if (myIsPrimitive == null) {
+ final Value value = getValue();
+ if (value != null) {
+ myIsPrimitive = super.isPrimitive();
+ }
+ else {
+ myIsPrimitive = DebuggerUtils.isPrimitiveType(myField.typeName()) ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+ return myIsPrimitive.booleanValue();
+ }
+
+ public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ try {
+ return (myObject != null) ? myObject.getValue(myField) : myField.declaringType().getValue(myField);
+ }
+ catch (ObjectCollectedException e) {
+ throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
+ }
+ }
+
+ public boolean isStatic() {
+ return myIsStatic;
+ }
+
+ public String getName() {
+ final String fieldName = myField.name();
+ if (isOuterLocalVariableValue() && NodeRendererSettings.getInstance().getClassRenderer().SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES) {
+ return StringUtil.trimStart(fieldName, OUTER_LOCAL_VAR_FIELD_PREFIX);
+ }
+ return fieldName;
+ }
+
+ public boolean isOuterLocalVariableValue() {
+ try {
+ return DebuggerUtils.isSynthetic(myField) && myField.name().startsWith(OUTER_LOCAL_VAR_FIELD_PREFIX);
+ }
+ catch (UnsupportedOperationException e) {
+ return false;
+ }
+ }
+
+ public String calcValueName() {
+ final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
+ StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ buf.append(getName());
+ if (classRenderer.SHOW_DECLARED_TYPE) {
+ buf.append(": ");
+ buf.append(classRenderer.renderTypeName(myField.typeName()));
+ }
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
+ String fieldName;
+ if(isStatic()) {
+ String typeName = myField.declaringType().name().replace('$', '.');
+ typeName = DebuggerTreeNodeExpression.normalize(typeName, PositionUtil.getContextElement(context), context.getProject());
+ fieldName = typeName + "." + getName();
+ }
+ else {
+ //noinspection HardCodedStringLiteral
+ fieldName = isOuterLocalVariableValue()? StringUtil.trimStart(getName(), OUTER_LOCAL_VAR_FIELD_PREFIX) : "this." + getName();
+ }
+ try {
+ return elementFactory.createExpressionFromText(fieldName, null);
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(DebuggerBundle.message("error.invalid.field.name", getName()), e);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java
new file mode 100644
index 0000000..96f0dc6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.tree.LocalVariableDescriptor;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.NotNull;
+
+public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements LocalVariableDescriptor {
+ private final StackFrameProxyImpl myFrameProxy;
+ private final LocalVariableProxyImpl myLocalVariable;
+
+ private String myTypeName = DebuggerBundle.message("label.unknown.value");
+ private boolean myIsPrimitive;
+
+ private boolean myIsNewLocal = true;
+ private boolean myIsVisible = true;
+
+ public LocalVariableDescriptorImpl(Project project,
+ @NotNull LocalVariableProxyImpl local) {
+ super(project);
+ setLvalue(true);
+ myFrameProxy = local.getFrame();
+ myLocalVariable = local;
+ }
+
+ public LocalVariableProxyImpl getLocalVariable() {
+ return myLocalVariable;
+ }
+
+ public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context) {
+ StackFrameProxyImpl frame = context.getFrameProxy();
+ if (frame == null) return null;
+
+ PsiElement place = PositionUtil.getContextElement(context);
+
+ if (place == null) {
+ return null;
+ }
+
+ PsiVariable psiVariable = JavaPsiFacade.getInstance(project).getResolveHelper().resolveReferencedVariable(getName(), place);
+ if (psiVariable == null) {
+ return null;
+ }
+
+ PsiFile containingFile = psiVariable.getContainingFile();
+ if(containingFile == null) return null;
+
+ return SourcePosition.createFromOffset(containingFile, psiVariable.getTextOffset());
+ }
+
+ public boolean isNewLocal() {
+ return myIsNewLocal;
+ }
+
+ public boolean isPrimitive() {
+ return myIsPrimitive;
+ }
+
+ public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
+ myIsVisible = myFrameProxy.isLocalVariableVisible(getLocalVariable());
+ if (myIsVisible) {
+ final String typeName = getLocalVariable().typeName();
+ myTypeName = typeName;
+ myIsPrimitive = DebuggerUtils.isPrimitiveType(typeName);
+ return myFrameProxy.getValue(getLocalVariable());
+ }
+
+ return null;
+ }
+
+ public void setNewLocal(boolean aNew) {
+ myIsNewLocal = aNew;
+ }
+
+ public void displayAs(NodeDescriptor descriptor) {
+ super.displayAs(descriptor);
+ if(descriptor instanceof LocalVariableDescriptorImpl) {
+ myIsNewLocal = ((LocalVariableDescriptorImpl)descriptor).myIsNewLocal;
+ }
+ }
+
+ public String getName() {
+ return myLocalVariable.name();
+ }
+
+ public String calcValueName() {
+ final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
+ StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ buf.append(getName());
+ if (classRenderer.SHOW_DECLARED_TYPE) {
+ buf.append(": ");
+ buf.append(classRenderer.renderTypeName(myTypeName));
+ }
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
+ try {
+ return elementFactory.createExpressionFromText(getName(), PositionUtil.getContextElement(context));
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(DebuggerBundle.message("error.invalid.local.variable.name", getName()), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MarkedDescriptorTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MarkedDescriptorTree.java
new file mode 100644
index 0000000..90203e5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MarkedDescriptorTree.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.impl.descriptors.data.DescriptorKey;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MarkedDescriptorTree {
+ private final HashMap<NodeDescriptor, Map<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor>> myChildrenMap = new HashMap<NodeDescriptor, Map<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor>>();
+ private final Map<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor> myRootChildren = new com.intellij.util.containers.HashMap<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor>();
+
+ public <T extends NodeDescriptor> void addChild(NodeDescriptor parent, T child, DescriptorKey<T> key) {
+ Map<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor> children;
+
+ if(parent == null) {
+ children = myRootChildren;
+ }
+ else {
+ children = myChildrenMap.get(parent);
+ if(children == null) {
+ children = new com.intellij.util.containers.HashMap<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor>();
+ myChildrenMap.put(parent, children);
+ }
+ }
+ children.put(key, child);
+ }
+
+ public <T extends NodeDescriptor> T getChild(NodeDescriptor parent, DescriptorKey<T> key) {
+ if(parent == null) {
+ return (T)myRootChildren.get(key);
+ }
+ final Map<DescriptorKey<? extends NodeDescriptor>, NodeDescriptor> map = myChildrenMap.get(parent);
+ return (T)(map != null ? map.get(key) : null);
+ }
+
+ public void clear() {
+ myChildrenMap.clear();
+ myRootChildren.clear();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MessageDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MessageDescriptor.java
new file mode 100644
index 0000000..026f2ad
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MessageDescriptor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
+
+public class MessageDescriptor extends NodeDescriptorImpl {
+ public static final int ERROR = 0;
+ public static final int WARNING = 1;
+ public static final int INFORMATION = 2;
+ public static final int SPECIAL = 3;
+ private int myKind;
+ private String myMessage;
+
+ public static MessageDescriptor DEBUG_INFO_UNAVAILABLE = new MessageDescriptor(DebuggerBundle.message("message.node.debug.info.not.available"));
+ public static MessageDescriptor LOCAL_VARIABLES_INFO_UNAVAILABLE = new MessageDescriptor(
+ DebuggerBundle.message("message.node.local.variables.debug.info.not.available")
+ );
+ public static MessageDescriptor ALL_ELEMENTS_IN_VISIBLE_RANGE_ARE_NULL = new MessageDescriptor(
+ DebuggerBundle.message("message.node.all.array.elements.null"));
+ public static MessageDescriptor ALL_ELEMENTS_IN_RANGE_ARE_NULL = new MessageDescriptor(
+ DebuggerBundle.message("message.node.all.elements.null"));
+ public static MessageDescriptor ARRAY_IS_EMPTY = new MessageDescriptor(DebuggerBundle.message("message.node.empty.array"));
+ public static MessageDescriptor CLASS_HAS_NO_FIELDS = new MessageDescriptor(DebuggerBundle.message("message.node.class.has.no.fields"));
+ public static MessageDescriptor OBJECT_COLLECTED = new MessageDescriptor(DebuggerBundle.message("message.node.object.collected"));
+ public static MessageDescriptor EVALUATING = new MessageDescriptor(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE);
+ public static MessageDescriptor THREAD_IS_RUNNING = new MessageDescriptor(DebuggerBundle.message("message.node.thread.running"));
+ public static MessageDescriptor THREAD_IS_EMPTY = new MessageDescriptor(DebuggerBundle.message("message.node.thread.has.no.frames"));
+ public static MessageDescriptor EVALUATION_NOT_POSSIBLE = new MessageDescriptor(DebuggerBundle.message("message.node.evaluation.not.possible", WARNING));
+
+ public MessageDescriptor(String message) {
+ this(message, INFORMATION);
+ }
+
+ public MessageDescriptor(String message, int kind) {
+ myKind = kind;
+ myMessage = message;
+ }
+
+ public int getKind() {
+ return myKind;
+ }
+
+ public String getLabel() {
+ return myMessage;
+ }
+
+ public boolean isExpandable() {
+ return false;
+ }
+
+ public void setContext(EvaluationContextImpl context) {
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return myMessage;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MethodReturnValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MethodReturnValueDescriptorImpl.java
new file mode 100644
index 0000000..2ab0b02
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MethodReturnValueDescriptorImpl.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.Method;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+
+/**
+ * User: lex
+ * Date: Oct 8, 2003
+ * Time: 5:08:07 PM
+ */
+public class MethodReturnValueDescriptorImpl extends ValueDescriptorImpl{
+ private final Method myMethod;
+ private final Value myValue;
+
+ public MethodReturnValueDescriptorImpl(Project project, final Method method, Value value) {
+ super(project);
+ myMethod = method;
+ myValue = value;
+ }
+
+ public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
+ return myValue;
+ }
+
+ public String getName() {
+ //noinspection HardCodedStringLiteral
+ return myMethod.toString();
+ }
+
+ public String calcValueName() {
+ return getName();
+ }
+
+ public Type getType() {
+ if (myValue == null) {
+ try {
+ return myMethod.returnType();
+ }
+ catch (ClassNotLoadedException ignored) {
+ }
+ }
+ return super.getType();
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ throw new EvaluateException("Evaluation not supported for method return value");
+ }
+
+ public boolean canSetValue() {
+ return false;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MethodsTracker.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MethodsTracker.java
new file mode 100644
index 0000000..8547e48
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/MethodsTracker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.sun.jdi.Method;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 13, 2006
+ */
+public class MethodsTracker {
+ private final Map<Method, Integer> myMethodToOccurrenceMap = new HashMap<Method, Integer>();
+
+ public final class MethodOccurrence {
+ private final Method myMethod;
+ private final int myIndex;
+
+ private MethodOccurrence(Method method, int index) {
+ myMethod = method;
+ myIndex = index;
+ }
+
+ public Method getMethod() {
+ return myMethod;
+ }
+
+ public int getIndex() {
+ return getOccurrenceCount(myMethod) - myIndex;
+ }
+
+ public boolean isRecursive() {
+ return getOccurrenceCount(myMethod) > 1;
+ }
+ }
+
+ public MethodOccurrence getMethodOccurrence(Method method) {
+ return new MethodOccurrence(method, assignOccurrenceIndex(method));
+ }
+
+ private int getOccurrenceCount(Method method) {
+ if (method == null) {
+ return 0;
+ }
+ final Integer integer = myMethodToOccurrenceMap.get(method);
+ return integer != null? integer.intValue(): 0;
+ }
+
+ private int assignOccurrenceIndex(Method method) {
+ if (method == null) {
+ return 0;
+ }
+ final int count = getOccurrenceCount(method);
+ myMethodToOccurrenceMap.put(method, count + 1);
+ return count;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java
new file mode 100644
index 0000000..9596b39
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.jdi.LocalVariableProxy;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
+import com.intellij.debugger.impl.descriptors.data.*;
+import com.intellij.debugger.jdi.LocalVariableProxyImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.NodeDescriptorFactory;
+import com.intellij.debugger.ui.tree.UserExpressionDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+
+public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.NodeDescriptorFactoryImpl");
+ private DescriptorTree myCurrentHistoryTree = new DescriptorTree(true);
+
+ private DescriptorTreeSearcher myDescriptorSearcher;
+ private DescriptorTreeSearcher myDisplayDescriptorSearcher;
+
+ protected final Project myProject;
+
+ public NodeDescriptorFactoryImpl(Project project) {
+ myProject = project;
+ myDescriptorSearcher = new DescriptorTreeSearcher(new MarkedDescriptorTree());
+ myDisplayDescriptorSearcher = new DisplayDescriptorTreeSearcher(new MarkedDescriptorTree());
+ }
+
+ public void dispose() {
+ myCurrentHistoryTree.clear();
+ myDescriptorSearcher.clear();
+ myDisplayDescriptorSearcher.clear();
+ }
+
+ private <T extends NodeDescriptor> T getDescriptor(NodeDescriptor parent, DescriptorData<T> key) {
+ final T descriptor = key.createDescriptor(myProject);
+
+ final T oldDescriptor = findDescriptor(parent, descriptor, key);
+
+ if(oldDescriptor != null && oldDescriptor.getClass() == descriptor.getClass()) {
+ descriptor.setAncestor(oldDescriptor);
+ }
+ else {
+ T displayDescriptor = findDisplayDescriptor(parent, descriptor, key.getDisplayKey());
+ if(displayDescriptor != null) {
+ descriptor.displayAs(displayDescriptor);
+ }
+ }
+
+ myCurrentHistoryTree.addChild(parent, descriptor);
+
+ return descriptor;
+ }
+
+ @Nullable
+ protected <T extends NodeDescriptor>T findDisplayDescriptor(NodeDescriptor parent, T descriptor, DisplayKey<T> key) {
+ return myDisplayDescriptorSearcher.search(parent, descriptor, key);
+ }
+
+ @Nullable
+ protected <T extends NodeDescriptor> T findDescriptor(NodeDescriptor parent, T descriptor, DescriptorData<T> key) {
+ return myDescriptorSearcher.search(parent, descriptor, key);
+ }
+
+ public DescriptorTree getCurrentHistoryTree() {
+ return myCurrentHistoryTree;
+ }
+
+ public void deriveHistoryTree(DescriptorTree tree, final StackFrameContext context) {
+
+ final MarkedDescriptorTree descriptorTree = new MarkedDescriptorTree();
+ final MarkedDescriptorTree displayDescriptorTree = new MarkedDescriptorTree();
+
+ tree.dfst(new DescriptorTree.DFSTWalker() {
+ public void visit(NodeDescriptor parent, NodeDescriptor child) {
+ final DescriptorData<NodeDescriptor> descriptorData = DescriptorData.getDescriptorData(child);
+ descriptorTree.addChild(parent, child, descriptorData);
+ displayDescriptorTree.addChild(parent, child, descriptorData.getDisplayKey());
+ }
+ });
+
+ myDescriptorSearcher = new DescriptorTreeSearcher(descriptorTree);
+ myDisplayDescriptorSearcher = new DisplayDescriptorTreeSearcher(displayDescriptorTree);
+
+ myCurrentHistoryTree = createDescriptorTree(context, tree);
+ }
+
+ private static DescriptorTree createDescriptorTree(final StackFrameContext context, final DescriptorTree fromTree) {
+ int frameCount = -1;
+ int frameIndex = -1;
+ final StackFrameProxy frameProxy = context.getFrameProxy();
+ if (frameProxy != null) {
+ try {
+ final ThreadReferenceProxy threadReferenceProxy = frameProxy.threadProxy();
+ frameCount = threadReferenceProxy.frameCount();
+ frameIndex = frameProxy.getFrameIndex();
+ }
+ catch (EvaluateException e) {
+ // ignored
+ }
+ }
+ final boolean isInitial = !fromTree.frameIdEquals(frameCount, frameIndex);
+ DescriptorTree descriptorTree = new DescriptorTree(isInitial);
+ descriptorTree.setFrameId(frameCount, frameIndex);
+ return descriptorTree;
+ }
+
+ public ArrayElementDescriptorImpl getArrayItemDescriptor(NodeDescriptor parent, ArrayReference array, int index) {
+ return getDescriptor(parent, new ArrayItemData(array, index));
+ }
+
+ public FieldDescriptorImpl getFieldDescriptor(NodeDescriptor parent, ObjectReference objRef, Field field) {
+ final DescriptorData<FieldDescriptorImpl> descriptorData;
+ if (objRef == null ) {
+ if (!field.isStatic()) {
+ LOG.error("Object reference is null for non-static field: " + field);
+ }
+ descriptorData = new StaticFieldData(field);
+ }
+ else {
+ descriptorData = new FieldData(objRef, field);
+ }
+ return getDescriptor(parent, descriptorData);
+ }
+
+ public LocalVariableDescriptorImpl getLocalVariableDescriptor(NodeDescriptor parent, LocalVariableProxy local) {
+ return getDescriptor(parent, new LocalData((LocalVariableProxyImpl)local));
+ }
+
+ public ArgumentValueDescriptorImpl getArgumentValueDescriptor(NodeDescriptor parent, int index, Value value) {
+ return getDescriptor(parent, new ArgValueData(index, value));
+ }
+
+ public StackFrameDescriptorImpl getStackFrameDescriptor(NodeDescriptorImpl parent, StackFrameProxyImpl frameProxy) {
+ return getDescriptor(parent, new StackFrameData(frameProxy));
+ }
+
+ public StaticDescriptorImpl getStaticDescriptor(NodeDescriptorImpl parent, ReferenceType refType) {//static is unique
+ return getDescriptor(parent, new StaticData(refType));
+ }
+
+ public ValueDescriptorImpl getThisDescriptor(NodeDescriptorImpl parent, Value value) {
+ return getDescriptor(parent, new ThisData());
+ }
+
+ public ValueDescriptorImpl getMethodReturnValueDescriptor(NodeDescriptorImpl parent, Method method, Value value) {
+ return getDescriptor(parent, new MethodReturnValueData(method, value));
+ }
+
+ public ThreadDescriptorImpl getThreadDescriptor(NodeDescriptorImpl parent, ThreadReferenceProxyImpl thread) {
+ return getDescriptor(parent, new ThreadData(thread));
+ }
+
+ public ThreadGroupDescriptorImpl getThreadGroupDescriptor(NodeDescriptorImpl parent, ThreadGroupReferenceProxyImpl group) {
+ return getDescriptor(parent, new ThreadGroupData(group));
+ }
+
+ public UserExpressionDescriptor getUserExpressionDescriptor(NodeDescriptor parent, final DescriptorData<UserExpressionDescriptor> data) {
+ return getDescriptor(parent, data);
+ }
+
+ public WatchItemDescriptor getWatchItemDescriptor(NodeDescriptor parent, TextWithImports text, @Nullable Value value){
+ return getDescriptor(parent, new WatchItemData(text, value));
+ }
+
+ private static class DescriptorTreeSearcher {
+ private final MarkedDescriptorTree myDescriportTree;
+
+ private final HashMap<NodeDescriptor, NodeDescriptor> mySearchedDescriptors = new HashMap<NodeDescriptor, NodeDescriptor>();
+
+ public DescriptorTreeSearcher(MarkedDescriptorTree descriportTree) {
+ myDescriportTree = descriportTree;
+ }
+
+ @Nullable
+ public <T extends NodeDescriptor> T search(NodeDescriptor parent, T descriptor, DescriptorKey<T> key) {
+ final T result;
+ if(parent == null) {
+ result = myDescriportTree.getChild(null, key);
+ }
+ else {
+ final NodeDescriptor parentDescriptor = getSearched(parent);
+ result = parentDescriptor != null ? myDescriportTree.getChild(parentDescriptor, key) : null;
+ }
+ if(result != null) {
+ mySearchedDescriptors.put(descriptor, result);
+ }
+ return result;
+ }
+
+ protected NodeDescriptor getSearched(NodeDescriptor parent) {
+ return mySearchedDescriptors.get(parent);
+ }
+
+ public void clear() {
+ mySearchedDescriptors.clear();
+ myDescriportTree.clear();
+ }
+ }
+
+ private class DisplayDescriptorTreeSearcher extends DescriptorTreeSearcher {
+ public DisplayDescriptorTreeSearcher(MarkedDescriptorTree descriportTree) {
+ super(descriportTree);
+ }
+
+ protected NodeDescriptor getSearched(NodeDescriptor parent) {
+ NodeDescriptor searched = super.getSearched(parent);
+ if(searched == null) {
+ return myDescriptorSearcher.getSearched(parent);
+ }
+ return searched;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorImpl.java
new file mode 100644
index 0000000..a009733
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorImpl.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Key;
+import com.intellij.util.containers.HashMap;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.sun.jdi.InconsistentDebugInfoException;
+import com.sun.jdi.InvalidStackFrameException;
+import com.sun.jdi.ObjectReference;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public abstract class NodeDescriptorImpl implements NodeDescriptor {
+ protected static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl");
+
+ public static final String UNKNOWN_VALUE_MESSAGE = "";
+ public boolean myIsExpanded = false;
+ public boolean myIsSelected = false;
+ public boolean myIsVisible = false;
+ public boolean myIsSynthetic = false;
+
+ private EvaluateException myEvaluateException;
+ private String myLabel = UNKNOWN_VALUE_MESSAGE;
+
+ private HashMap<Key, Object> myUserData;
+
+ private final List<NodeDescriptorImpl> myChildren = new ArrayList<NodeDescriptorImpl>();
+ private static final Key<Map<ObjectReference, ValueMarkup>> MARKUP_MAP_KEY = new Key<Map<ObjectReference, ValueMarkup>>("ValueMarkupMap");
+
+ public String getName() {
+ return null;
+ }
+
+ public <T> T getUserData(Key<T> key) {
+ if(myUserData == null) return null;
+ return (T) myUserData.get(key);
+ }
+
+ public <T> void putUserData(Key<T> key, T value) {
+ if(myUserData == null) {
+ myUserData = new HashMap<Key, Object>();
+ }
+ myUserData.put(key, value);
+ }
+
+ public void updateRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener){
+ updateRepresentationNoNotify(context, labelListener);
+ labelListener.labelChanged();
+ }
+
+ protected void updateRepresentationNoNotify(EvaluationContextImpl context, DescriptorLabelListener labelListener) {
+ try {
+ try {
+ myEvaluateException = null;
+ myLabel = calcRepresentation(context, labelListener);
+ }
+ catch (RuntimeException e) {
+ LOG.debug(e);
+ throw processException(e);
+ }
+ }
+ catch (EvaluateException e) {
+ setFailed(e);
+ }
+ }
+
+ protected abstract String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener) throws EvaluateException;
+
+ private EvaluateException processException(Exception e) {
+ if(e instanceof InconsistentDebugInfoException) {
+ return new EvaluateException(DebuggerBundle.message("error.inconsistent.debug.info"), null);
+ }
+
+ else if(e instanceof InvalidStackFrameException) {
+ return new EvaluateException(DebuggerBundle.message("error.invalid.stackframe"), null);
+ }
+ else {
+ return EvaluateExceptionUtil.DEBUG_INFO_UNAVAILABLE;
+ }
+ }
+
+ public void displayAs(NodeDescriptor descriptor) {
+ if (descriptor instanceof NodeDescriptorImpl) {
+ final NodeDescriptorImpl that = (NodeDescriptorImpl)descriptor;
+ myIsExpanded = that.myIsExpanded;
+ myIsSelected = that.myIsSelected;
+ myIsVisible = that.myIsVisible;
+ myUserData = that.myUserData != null ? new HashMap<Key, Object>(that.myUserData) : null;
+ }
+ }
+
+ public abstract boolean isExpandable();
+
+ public abstract void setContext(EvaluationContextImpl context);
+
+ public EvaluateException getEvaluateException() {
+ return myEvaluateException;
+ }
+
+ public String getLabel() {
+ return myLabel;
+ }
+
+ public String toString() {
+ return getLabel();
+ }
+
+ protected String setFailed(EvaluateException e) {
+ myEvaluateException = e;
+ return e.getMessage();
+ }
+
+ protected String setLabel(String customLabel) {
+ return myLabel = customLabel;
+ }
+
+ //Context is set to null
+ public void clear() {
+ myEvaluateException = null;
+ myLabel = "";
+ }
+
+ public List<NodeDescriptorImpl> getChildren() {
+ return myChildren;
+ }
+
+ public void setAncestor(NodeDescriptor oldDescriptor) {
+ displayAs(oldDescriptor);
+ }
+
+ @Nullable
+ public static Map<ObjectReference, ValueMarkup> getMarkupMap(final DebugProcess process) {
+ if (process == null) {
+ return null;
+ }
+ Map<ObjectReference, ValueMarkup> map = process.getUserData(MARKUP_MAP_KEY);
+ if (map == null) {
+ map = new java.util.HashMap<ObjectReference, ValueMarkup>();
+ process.putUserData(MARKUP_MAP_KEY, map);
+ }
+ return map;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java
new file mode 100644
index 0000000..905f7b0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.ui.impl.nodes.NodeComparator;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.NodeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.HashMap;
+import com.sun.jdi.Location;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ ** finds correspondence between new descriptor and one created on the previous steps
+ ** stores maximum CACHED_STEPS steps
+ ** call saveState function to start new step
+ */
+
+public class NodeManagerImpl extends NodeDescriptorFactoryImpl implements NodeManager{
+ private static final Comparator<DebuggerTreeNode> ourNodeComparator = new NodeComparator();
+
+ private final DebuggerTree myDebuggerTree;
+ private String myHistoryKey = null;
+ private final Map<String, DescriptorTree> myHistories = new HashMap<String, DescriptorTree>();
+
+ public NodeManagerImpl(Project project, DebuggerTree tree) {
+ super(project);
+ myDebuggerTree = tree;
+ }
+
+ public static Comparator<DebuggerTreeNode> getNodeComparator() {
+ return ourNodeComparator;
+ }
+
+ public DebuggerTreeNodeImpl createNode(NodeDescriptor descriptor, EvaluationContext evaluationContext) {
+ ((NodeDescriptorImpl)descriptor).setContext((EvaluationContextImpl)evaluationContext);
+ return DebuggerTreeNodeImpl.createNode(getTree(), (NodeDescriptorImpl)descriptor, (EvaluationContextImpl)evaluationContext);
+ }
+
+ public DebuggerTreeNodeImpl getDefaultNode() {
+ return DebuggerTreeNodeImpl.createNodeNoUpdate(getTree(), new DefaultNodeDescriptor());
+ }
+
+ public DebuggerTreeNodeImpl createMessageNode(MessageDescriptor descriptor) {
+ return DebuggerTreeNodeImpl.createNodeNoUpdate(getTree(), descriptor);
+ }
+
+ public DebuggerTreeNodeImpl createMessageNode(String message) {
+ return DebuggerTreeNodeImpl.createNodeNoUpdate(getTree(), new MessageDescriptor(message));
+ }
+
+ public void setHistoryByContext(final DebuggerContextImpl context) {
+ if (myHistoryKey != null) {
+ myHistories.put(myHistoryKey, getCurrentHistoryTree());
+ }
+
+ final String historyKey = getContextKey(context.getFrameProxy());
+ final DescriptorTree descriptorTree;
+ if (historyKey != null) {
+ final DescriptorTree historyTree = myHistories.get(historyKey);
+ descriptorTree = (historyTree != null)? historyTree : new DescriptorTree(true);
+ }
+ else {
+ descriptorTree = new DescriptorTree(true);
+ }
+
+ deriveHistoryTree(descriptorTree, context);
+ myHistoryKey = historyKey;
+ }
+
+
+ @Nullable
+ public String getContextKey(final StackFrameProxyImpl frame) {
+ return getContextKeyForFrame(frame);
+ }
+
+ @Nullable
+ public static String getContextKeyForFrame(final StackFrameProxyImpl frame) {
+ if (frame == null) {
+ return null;
+ }
+ try {
+ final Location location = frame.location();
+ final Method method = location.method();
+ final ReferenceType referenceType = location.declaringType();
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ return builder.append(referenceType.signature()).append("#").append(method.name()).append(method.signature()).toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+ catch (EvaluateException e) {
+ return null;
+ }
+ }
+
+ public void dispose() {
+ myHistories.clear();
+ super.dispose();
+ }
+
+ private DebuggerTree getTree() {
+ return myDebuggerTree;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java
new file mode 100644
index 0000000..41a5384
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.ContextUtil;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.settings.ThreadsViewSettings;
+import com.intellij.debugger.ui.tree.StackFrameDescriptor;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.ui.FileColorManager;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Map;
+
+/**
+ * Nodes of this type cannot be updated, because StackFrame objects become invalid as soon as VM has been resumed
+ */
+public class StackFrameDescriptorImpl extends NodeDescriptorImpl implements StackFrameDescriptor{
+ private final StackFrameProxyImpl myFrame;
+ private int myUiIndex;
+ private String myName = null;
+ private Location myLocation;
+ private MethodsTracker.MethodOccurrence myMethodOccurrence;
+ private boolean myIsSynthetic;
+ private boolean myIsInLibraryContent;
+ private ObjectReference myThisObject;
+ private Color myBackgroundColor;
+
+ private Icon myIcon = AllIcons.Debugger.StackFrame;
+
+ public StackFrameDescriptorImpl(StackFrameProxyImpl frame, final MethodsTracker tracker) {
+ myFrame = frame;
+ try {
+ myUiIndex = frame.getFrameIndex();
+ myLocation = frame.location();
+ myThisObject = frame.thisObject();
+ myMethodOccurrence = tracker.getMethodOccurrence(myLocation.method());
+ myIsSynthetic = DebuggerUtils.isSynthetic(myMethodOccurrence.getMethod());
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final SourcePosition position = ContextUtil.getSourcePosition(StackFrameDescriptorImpl.this);
+ final PsiFile file = position != null? position.getFile() : null;
+ if (file == null) {
+ myIsInLibraryContent = true;
+ }
+ else {
+ myBackgroundColor = FileColorManager.getInstance(file.getProject()).getFileColor(file);
+
+ final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(getDebugProcess().getProject()).getFileIndex();
+ final VirtualFile vFile = file.getVirtualFile();
+ myIsInLibraryContent = vFile != null && (projectFileIndex.isInLibraryClasses(vFile) || projectFileIndex.isInLibrarySource(vFile));
+ }
+ }
+ });
+ }
+ catch(InternalException e) {
+ LOG.info(e);
+ myLocation = null;
+ myMethodOccurrence = tracker.getMethodOccurrence(null);
+ myIsSynthetic = false;
+ myIsInLibraryContent = false;
+ }
+ catch (EvaluateException e) {
+ LOG.info(e);
+ myLocation = null;
+ myMethodOccurrence = tracker.getMethodOccurrence(null);
+ myIsSynthetic = false;
+ myIsInLibraryContent = false;
+ }
+ }
+
+ public int getUiIndex() {
+ return myUiIndex;
+ }
+
+ public StackFrameProxyImpl getFrameProxy() {
+ return myFrame;
+ }
+
+ public DebugProcess getDebugProcess() {
+ return myFrame.getVirtualMachine().getDebugProcess();
+ }
+
+ @Override
+ public Color getBackgroundColor() {
+ return myBackgroundColor;
+ }
+
+ @Nullable
+ public Method getMethod() {
+ return myMethodOccurrence.getMethod();
+ }
+
+ public int getOccurrenceIndex() {
+ return myMethodOccurrence.getIndex();
+ }
+
+ public boolean isRecursiveCall() {
+ return myMethodOccurrence.isRecursive();
+ }
+
+ @Nullable
+ public ValueMarkup getValueMarkup() {
+ if (myThisObject != null) {
+ final Map<ObjectReference, ValueMarkup> markupMap = getMarkupMap(myFrame.getVirtualMachine().getDebugProcess());
+ if (markupMap != null) {
+ return markupMap.get(myThisObject);
+ }
+ }
+ return null;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener descriptorLabelListener) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (myLocation == null) {
+ return "";
+ }
+ ThreadsViewSettings settings = ThreadsViewSettings.getInstance();
+ final StringBuilder label = StringBuilderSpinAllocator.alloc();
+ try {
+ Method method = myMethodOccurrence.getMethod();
+ if (method != null) {
+ myName = method.name();
+ label.append(myName);
+ label.append("()");
+ }
+ if (settings.SHOW_LINE_NUMBER) {
+ String lineNumber = null;
+ try {
+ lineNumber = Integer.toString(myLocation.lineNumber());
+ }
+ catch (InternalError e) {
+ lineNumber = e.toString();
+ }
+ if (lineNumber != null) {
+ label.append(':');
+ label.append(lineNumber);
+ }
+ }
+ if (settings.SHOW_CLASS_NAME) {
+ String name = null;
+ try {
+ ReferenceType refType = myLocation.declaringType();
+ name = refType != null ? refType.name() : null;
+ }
+ catch (InternalError e) {
+ name = e.toString();
+ }
+ if (name != null) {
+ label.append(", ");
+ int dotIndex = name.lastIndexOf('.');
+ if (dotIndex < 0) {
+ label.append(name);
+ }
+ else {
+ label.append(name.substring(dotIndex + 1));
+ label.append(" {");
+ label.append(name.substring(0, dotIndex));
+ label.append("}");
+ }
+ }
+ }
+ if (settings.SHOW_SOURCE_NAME) {
+ try {
+ String sourceName;
+ try {
+ sourceName = myLocation.sourceName();
+ }
+ catch (InternalError e) {
+ sourceName = e.toString();
+ }
+ label.append(", ");
+ label.append(sourceName);
+ }
+ catch (AbsentInformationException exception) {
+ }
+ }
+ return label.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(label);
+ }
+ }
+
+ public final boolean stackFramesEqual(StackFrameDescriptorImpl d) {
+ return getFrameProxy().equals(d.getFrameProxy());
+ }
+
+ public boolean isExpandable() {
+ return true;
+ }
+
+ public final void setContext(EvaluationContextImpl context) {
+ myIcon = calcIcon();
+ }
+
+ public boolean isSynthetic() {
+ return myIsSynthetic;
+ }
+
+ public boolean isInLibraryContent() {
+ return myIsInLibraryContent;
+ }
+
+ public Location getLocation() {
+ return myLocation;
+ }
+
+ private Icon calcIcon() {
+ try {
+ if(myFrame.isObsolete()) {
+ return AllIcons.Debugger.Db_obsolete;
+ }
+ }
+ catch (EvaluateException e) {
+ }
+ return AllIcons.Debugger.StackFrame;
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StaticDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StaticDescriptorImpl.java
new file mode 100644
index 0000000..6284de4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StaticDescriptorImpl.java
@@ -0,0 +1,72 @@
+/*
+ * 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 StaticDescriptorImpl
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.tree.StaticDescriptor;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.sun.jdi.Field;
+import com.sun.jdi.ReferenceType;
+
+public class StaticDescriptorImpl extends NodeDescriptorImpl implements StaticDescriptor{
+
+ private final ReferenceType myType;
+ private final boolean myHasStaticFields;
+
+ public StaticDescriptorImpl(ReferenceType refType) {
+ myType = refType;
+
+ boolean hasStaticFields = false;
+ for (Field field : myType.allFields()) {
+ if (field.isStatic()) {
+ hasStaticFields = true;
+ break;
+ }
+ }
+ myHasStaticFields = hasStaticFields;
+ }
+
+ public ReferenceType getType() {
+ return myType;
+ }
+
+ public String getName() {
+ //noinspection HardCodedStringLiteral
+ return "static";
+ }
+
+ public boolean isExpandable() {
+ return myHasStaticFields;
+ }
+
+ public void setContext(EvaluationContextImpl context) {
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener descriptorLabelListener) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
+ return getName() + " = " + classRenderer.renderTypeName(myType.name());
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java
new file mode 100644
index 0000000..67e8304
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.Value;
+
+/**
+ * User: lex
+ * Date: Oct 8, 2003
+ * Time: 5:08:07 PM
+ */
+public class ThisDescriptorImpl extends ValueDescriptorImpl{
+
+ public ThisDescriptorImpl(Project project) {
+ super(project);
+ }
+
+ public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
+ return evaluationContext != null? evaluationContext.getThisObject() : null;
+ }
+
+ public String getName() {
+ //noinspection HardCodedStringLiteral
+ return "this";
+ }
+
+ public String calcValueName() {
+ return getName();
+ }
+
+ public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
+ try {
+ return elementFactory.createExpressionFromText("this", null);
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(e.getMessage(), e);
+ }
+ }
+
+ public boolean canSetValue() {
+ return false;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java
new file mode 100644
index 0000000..dd50562
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.SuspendManager;
+import com.intellij.debugger.engine.SuspendManagerUtil;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.tree.ThreadDescriptor;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.icons.AllIcons;
+import com.sun.jdi.ObjectCollectedException;
+import com.sun.jdi.ThreadReference;
+
+import javax.swing.*;
+
+public class ThreadDescriptorImpl extends NodeDescriptorImpl implements ThreadDescriptor{
+ private final ThreadReferenceProxyImpl myThread;
+ private String myName = null;
+ private boolean myIsExpandable = true;
+ private boolean myIsSuspended = false;
+ private boolean myIsCurrent;
+ private boolean myIsFrozen;
+
+ private boolean myIsAtBreakpoint;
+ private SuspendContextImpl mySuspendContext;
+
+ public ThreadDescriptorImpl(ThreadReferenceProxyImpl thread) {
+ myThread = thread;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ ThreadReferenceProxyImpl thread = getThreadReference();
+ try {
+ myName = thread.name();
+ ThreadGroupReferenceProxyImpl gr = getThreadReference().threadGroupProxy();
+ final String grname = (gr != null)? gr.name() : null;
+ final String threadStatusText = DebuggerUtilsEx.getThreadStatusText(getThreadReference().status());
+ //noinspection HardCodedStringLiteral
+ if (grname != null && !"SYSTEM".equalsIgnoreCase(grname)) {
+ return DebuggerBundle.message("label.thread.node.in.group", myName, thread.uniqueID(), threadStatusText, grname);
+ }
+ return DebuggerBundle.message("label.thread.node", myName, thread.uniqueID(), threadStatusText);
+ }
+ catch (ObjectCollectedException e) {
+ return myName != null ? DebuggerBundle.message("label.thread.node.thread.collected", myName) : "";
+ }
+ }
+
+ public ThreadReferenceProxyImpl getThreadReference() {
+ return myThread;
+ }
+
+ public boolean isCurrent() {
+ return myIsCurrent;
+ }
+
+ public boolean isFrozen() {
+ return myIsFrozen;
+ }
+
+ public boolean isExpandable() {
+ return myIsExpandable;
+ }
+
+ public void setContext(EvaluationContextImpl context) {
+ final ThreadReferenceProxyImpl thread = getThreadReference();
+ final SuspendManager suspendManager = context != null? context.getDebugProcess().getSuspendManager() : null;
+ final SuspendContextImpl suspendContext = context != null? context.getSuspendContext() : null;
+
+ try {
+ myIsSuspended = suspendManager != null? suspendManager.isSuspended(thread) : thread.isSuspended();
+ }
+ catch (ObjectCollectedException e) {
+ myIsSuspended = false;
+ }
+ myIsExpandable = calcExpandable(myIsSuspended);
+ mySuspendContext = SuspendManagerUtil.getSuspendContextForThread(suspendContext, thread);
+ myIsAtBreakpoint = suspendManager != null? SuspendManagerUtil.findContextByThread(suspendManager, thread) != null : thread.getThreadReference().isAtBreakpoint();
+ myIsCurrent = suspendContext != null? suspendContext.getThread() == thread : false;
+ myIsFrozen = suspendManager != null? suspendManager.isFrozen(thread) : myIsSuspended;
+ }
+
+ private boolean calcExpandable(final boolean isSuspended) {
+ if (!isSuspended) {
+ return false;
+ }
+ final int status = getThreadReference().status();
+ if (status == ThreadReference.THREAD_STATUS_UNKNOWN ||
+ status == ThreadReference.THREAD_STATUS_NOT_STARTED ||
+ status == ThreadReference.THREAD_STATUS_ZOMBIE) {
+ return false;
+ }
+ return true;
+ /*
+ // [jeka] with lots of threads calling threadProxy.frameCount() in advance while setting context can be costly....
+ // see IDEADEV-2020
+ try {
+ return threadProxy.frameCount() > 0;
+ }
+ catch (EvaluateException e) {
+ //LOG.assertTrue(false);
+ // if we pause during evaluation of this method the exception is thrown
+ // private static void longMethod(){
+ // try {
+ // Thread.sleep(100000);
+ // } catch (InterruptedException e) {
+ // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ // }
+ // }
+ return false;
+ }
+ */
+ }
+
+ public SuspendContextImpl getSuspendContext() {
+ return mySuspendContext;
+ }
+
+ public boolean isAtBreakpoint() {
+ return myIsAtBreakpoint;
+ }
+
+ public boolean isSuspended() {
+ return myIsSuspended;
+ }
+
+ public Icon getIcon() {
+ if(isCurrent()) {
+ return AllIcons.Debugger.ThreadCurrent;
+ }
+ if(isFrozen()) {
+ return AllIcons.Debugger.ThreadFrozen;
+ }
+ if(isAtBreakpoint()) {
+ return AllIcons.Debugger.ThreadAtBreakpoint;
+ }
+ if(isSuspended()) {
+ return AllIcons.Debugger.ThreadSuspended;
+ }
+ return AllIcons.Debugger.ThreadRunning;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java
new file mode 100644
index 0000000..2f0b304
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
+import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
+import com.intellij.debugger.ui.tree.ThreadGroupDescriptor;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.sun.jdi.ObjectCollectedException;
+
+public class ThreadGroupDescriptorImpl extends NodeDescriptorImpl implements ThreadGroupDescriptor{
+ private final ThreadGroupReferenceProxyImpl myThreadGroup;
+ private boolean myIsCurrent;
+ private String myName = null;
+ private boolean myIsExpandable = true;
+
+ public ThreadGroupDescriptorImpl(ThreadGroupReferenceProxyImpl threadGroup) {
+ myThreadGroup = threadGroup;
+ }
+
+ public ThreadGroupReferenceProxyImpl getThreadGroupReference() {
+ return myThreadGroup;
+ }
+
+ public boolean isCurrent() {
+ return myIsCurrent;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener) throws EvaluateException {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ ThreadGroupReferenceProxyImpl group = getThreadGroupReference();
+ try {
+ myName = group.name();
+ return DebuggerBundle.message("label.thread.group.node", myName, group.uniqueID());
+ }
+ catch (ObjectCollectedException e) {
+ return myName != null ? DebuggerBundle.message("label.thread.group.node.group.collected", myName) : "";
+ }
+ }
+
+ public boolean isExpandable() {
+ return myIsExpandable;
+ }
+
+ public void setContext(EvaluationContextImpl context) {
+ ThreadReferenceProxyImpl threadProxy = context != null? context.getSuspendContext().getThread() : null;
+ myIsCurrent = threadProxy != null && isDescendantGroup(threadProxy.threadGroupProxy());
+ myIsExpandable = calcExpandable();
+ }
+
+ private boolean isDescendantGroup(ThreadGroupReferenceProxyImpl group) {
+ if(group == null) return false;
+
+ if(getThreadGroupReference() == group) return true;
+
+ return isDescendantGroup(group.parent());
+ }
+
+ private boolean calcExpandable() {
+ ThreadGroupReferenceProxyImpl group = getThreadGroupReference();
+ return group.threads().size() > 0 || group.threadGroups().size() > 0;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java
new file mode 100644
index 0000000..c3dbed6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java
@@ -0,0 +1,106 @@
+/*
+ * 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 StaticDescriptorImpl
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.tree.UserExpressionDescriptor;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiCodeFragment;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.Value;
+
+public class UserExpressionDescriptorImpl extends EvaluationDescriptor implements UserExpressionDescriptor{
+ private final ValueDescriptorImpl myParentDescriptor;
+ private final String myTypeName;
+ private final String myName;
+
+ public UserExpressionDescriptorImpl(Project project, ValueDescriptorImpl parent, String typeName, String name, TextWithImports text) {
+ super(text, project);
+ myParentDescriptor = parent;
+ myTypeName = typeName;
+ myName = name;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public String calcValueName() {
+ StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ buffer.append(getName());
+ buffer.append(": ");
+ final Value value = getValue();
+ if(value != null) {
+ final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
+ buffer.append(classRenderer.renderTypeName(value.type().name()));
+ }
+
+ return buffer.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ protected PsiCodeFragment getEvaluationCode(final StackFrameContext context) throws EvaluateException {
+ Value value = myParentDescriptor.getValue();
+
+ if(value instanceof ObjectReference) {
+ final String typeName = value.type().name();
+
+ final PsiClass psiClass = DebuggerUtilsEx.findClass(myTypeName, myProject, context.getDebugProcess().getSearchScope());
+
+ if (psiClass == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.type.name", typeName));
+ }
+
+ final PsiCodeFragment fragment =
+ getEffectiveCodeFragmentFactory(psiClass).createCodeFragment(getEvaluationText(), psiClass, myProject);
+ fragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
+ return fragment;
+ }
+ else {
+ throw EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("evaluation.error.objref.expected", myParentDescriptor.getName())
+ );
+ }
+ }
+
+ public ValueDescriptorImpl getParentDescriptor() {
+ return myParentDescriptor;
+ }
+
+ protected EvaluationContextImpl getEvaluationContext(final EvaluationContextImpl evaluationContext) {
+ return evaluationContext.createEvaluationContext(myParentDescriptor.getValue());
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java
new file mode 100644
index 0000000..3e11145
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java
@@ -0,0 +1,464 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.Patches;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.debugger.ui.tree.render.ClassRenderer;
+import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.debugger.ui.tree.render.Renderer;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiExpression;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.concurrency.Semaphore;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements ValueDescriptor{
+ protected final Project myProject;
+
+ NodeRenderer myRenderer = null;
+
+ NodeRenderer myAutoRenderer = null;
+
+ private Value myValue;
+ private EvaluateException myValueException;
+ protected EvaluationContextImpl myStoredEvaluationContext = null;
+
+ private String myValueLabel;
+ @Nullable
+ private Icon myValueIcon;
+
+ protected boolean myIsNew = true;
+ private boolean myIsDirty = false;
+ private boolean myIsLvalue = false;
+ private boolean myIsExpandable;
+
+ private boolean myShowIdLabel = true;
+
+ protected ValueDescriptorImpl(Project project, Value value) {
+ myProject = project;
+ myValue = value;
+ }
+
+ protected ValueDescriptorImpl(Project project) {
+ myProject = project;
+ }
+
+ public boolean isArray() {
+ return myValue instanceof ArrayReference;
+ }
+
+ public boolean isDirty() {
+ return myIsDirty;
+ }
+
+ public boolean isLvalue() {
+ return myIsLvalue;
+ }
+
+ public boolean isNull() {
+ return myValue == null;
+ }
+
+ @Override
+ public boolean isString() {
+ return myValue instanceof StringReference;
+ }
+
+ public boolean isPrimitive() {
+ return myValue instanceof PrimitiveValue;
+ }
+
+ public boolean isValueValid() {
+ return myValueException == null;
+ }
+
+ public boolean isShowIdLabel() {
+ return myShowIdLabel;
+ }
+
+ public void setShowIdLabel(boolean showIdLabel) {
+ myShowIdLabel = showIdLabel;
+ }
+
+ public Value getValue() {
+ // the following code makes sense only if we do not use ObjectReference.enableCollection() / disableCollection()
+ // to keep temporary objects
+ if (Patches.IBM_JDK_DISABLE_COLLECTION_BUG && myStoredEvaluationContext != null && !myStoredEvaluationContext.getSuspendContext().isResumed() &&
+ myValue instanceof ObjectReference && VirtualMachineProxyImpl.isCollected((ObjectReference)myValue)) {
+
+ final Semaphore semaphore = new Semaphore();
+ semaphore.down();
+ myStoredEvaluationContext.getDebugProcess().getManagerThread().invoke(new SuspendContextCommandImpl(myStoredEvaluationContext.getSuspendContext()) {
+ public void contextAction() throws Exception {
+ // re-setting the context will cause value recalculation
+ try {
+ setContext(myStoredEvaluationContext);
+ }
+ finally {
+ semaphore.up();
+ }
+ }
+ });
+ semaphore.waitFor();
+ }
+
+ return myValue;
+ }
+
+ public boolean isExpandable() {
+ return myIsExpandable;
+ }
+
+ public abstract Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException;
+
+ public final void setContext(EvaluationContextImpl evaluationContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ if (Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
+ myStoredEvaluationContext = evaluationContext;
+ }
+ Value value;
+ try {
+ value = calcValue(evaluationContext);
+
+ if(!myIsNew) {
+ try {
+ if (myValue instanceof DoubleValue && Double.isNaN(((DoubleValue)myValue).doubleValue())) {
+ myIsDirty = !(value instanceof DoubleValue);
+ }
+ else if (myValue instanceof FloatValue && Float.isNaN(((FloatValue)myValue).floatValue())) {
+ myIsDirty = !(value instanceof FloatValue);
+ }
+ else {
+ myIsDirty = (value == null) ? myValue != null : !value.equals(myValue);
+ }
+ }
+ catch (ObjectCollectedException e) {
+ myIsDirty = true;
+ }
+ }
+ myValue = value;
+ myValueException = null;
+ }
+ catch (EvaluateException e) {
+ myValueException = e;
+ myValue = getTargetExceptionWithStackTraceFilled(evaluationContext, e);
+ myIsExpandable = false;
+ }
+
+ myIsNew = false;
+ }
+
+ @Nullable
+ private static ObjectReference getTargetExceptionWithStackTraceFilled(final EvaluationContextImpl evaluationContext, EvaluateException ex){
+ final ObjectReference exceptionObj = ex.getExceptionFromTargetVM();
+ if (exceptionObj != null && evaluationContext != null) {
+ try {
+ final ReferenceType refType = exceptionObj.referenceType();
+ final List<Method> methods = refType.methodsByName("getStackTrace", "()[Ljava/lang/StackTraceElement;");
+ if (methods.size() > 0) {
+ final DebugProcessImpl process = evaluationContext.getDebugProcess();
+ process.invokeMethod(evaluationContext, exceptionObj, methods.get(0), Collections.emptyList());
+
+ // print to console as well
+
+ final Field traceField = refType.fieldByName("stackTrace");
+ final Value trace = traceField != null? exceptionObj.getValue(traceField) : null;
+ if (trace instanceof ArrayReference) {
+ final ArrayReference traceArray = (ArrayReference)trace;
+ final Type componentType = ((ArrayType)traceArray.referenceType()).componentType();
+ if (componentType instanceof ClassType) {
+ process.printToConsole(DebuggerUtils.getValueAsString(evaluationContext, exceptionObj));
+ process.printToConsole("\n");
+ for (Value stackElement : traceArray.getValues()) {
+ process.printToConsole("\tat ");
+ process.printToConsole(DebuggerUtils.getValueAsString(evaluationContext, stackElement));
+ process.printToConsole("\n");
+ }
+ }
+ }
+ }
+ }
+ catch (EvaluateException ignored) {
+ }
+ catch (ClassNotLoadedException ignored) {
+ }
+ }
+ return exceptionObj;
+ }
+
+ public void setAncestor(NodeDescriptor oldDescriptor) {
+ super.setAncestor(oldDescriptor);
+ myIsNew = false;
+ myValue = ((ValueDescriptorImpl)oldDescriptor).getValue();
+ }
+
+ protected void setLvalue(boolean value) {
+ myIsLvalue = value;
+ }
+
+ protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener){
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+
+ final NodeRenderer renderer = getRenderer(context.getDebugProcess());
+
+ final EvaluateException valueException = myValueException;
+ myIsExpandable = (valueException == null || valueException.getExceptionFromTargetVM() != null) && renderer.isExpandable(getValue(), context, this);
+
+ try {
+ setValueIcon(renderer.calcValueIcon(this, context, labelListener));
+ }
+ catch (EvaluateException e) {
+ LOG.info(e);
+ setValueIcon(null);
+ }
+
+ String label;
+ if (valueException == null) {
+ try {
+ label = renderer.calcLabel(this, context, labelListener);
+ }
+ catch (EvaluateException e) {
+ label = setValueLabelFailed(e);
+ }
+ }
+ else {
+ label = setValueLabelFailed(valueException);
+ }
+
+ return setValueLabel(label);
+ }
+
+ private String getCustomLabel(String label) {
+ //translate only strings in quotes
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ final Value value = getValue();
+ if(isShowIdLabel() && value instanceof ObjectReference) {
+ final String idLabel = getIdLabel((ObjectReference)value);
+ if(!label.startsWith(idLabel)) {
+ buf.append(idLabel);
+ }
+ }
+ if(label == null) {
+ //noinspection HardCodedStringLiteral
+ buf.append("null");
+ }
+ else {
+ buf.append(label);
+ }
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+
+ public String setValueLabel(String label) {
+ final String customLabel = getCustomLabel(label);
+ myValueLabel = customLabel;
+ return setLabel(calcValueName() + " = " + customLabel);
+ }
+
+ public String setValueLabelFailed(EvaluateException e) {
+ final String label = setFailed(e);
+ setValueLabel(label);
+ return label;
+ }
+
+ public Icon setValueIcon(Icon icon) {
+ return myValueIcon = icon;
+ }
+
+ @Nullable
+ public Icon getValueIcon() {
+ return myValueIcon;
+ }
+
+ public abstract String calcValueName();
+
+ public void displayAs(NodeDescriptor descriptor) {
+ if (descriptor instanceof ValueDescriptorImpl) {
+ ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
+ myRenderer = valueDescriptor.myRenderer;
+ }
+ super.displayAs(descriptor);
+ }
+
+ public Renderer getLastRenderer() {
+ return myRenderer != null ? myRenderer: myAutoRenderer;
+ }
+
+ @Nullable
+ public Type getType() {
+ Value value = getValue();
+ return value != null ? value.type() : null;
+ }
+
+ public NodeRenderer getRenderer (DebugProcessImpl debugProcess) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ Type type = getType();
+ if(type != null && myRenderer != null && myRenderer.isApplicable(type)) {
+ return myRenderer;
+ }
+
+ myAutoRenderer = debugProcess.getAutoRenderer(this);
+ return myAutoRenderer;
+ }
+
+ public void setRenderer(NodeRenderer renderer) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ myRenderer = renderer;
+ myAutoRenderer = null;
+ }
+
+ //returns expression that evaluates tree to this descriptor
+ public PsiExpression getTreeEvaluation(DebuggerTreeNodeImpl debuggerTreeNode, DebuggerContextImpl context) throws EvaluateException {
+ if(debuggerTreeNode.getParent() != null && debuggerTreeNode.getParent().getDescriptor() instanceof ValueDescriptor) {
+ final NodeDescriptorImpl descriptor = debuggerTreeNode.getParent().getDescriptor();
+ final ValueDescriptorImpl vDescriptor = ((ValueDescriptorImpl)descriptor);
+ final PsiExpression parentEvaluation = vDescriptor.getTreeEvaluation(debuggerTreeNode.getParent(), context);
+
+ if (parentEvaluation == null) {
+ return null;
+ }
+
+ return DebuggerTreeNodeExpression.substituteThis(
+ vDescriptor.getRenderer(context.getDebugProcess()).getChildValueExpression(debuggerTreeNode, context),
+ parentEvaluation, vDescriptor.getValue()
+ );
+ }
+
+ return getDescriptorEvaluation(context);
+ }
+
+ //returns expression that evaluates descriptor value
+ //use 'this' to reference parent node
+ //for ex. FieldDescriptorImpl should return
+ //this.fieldName
+ public abstract PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException;
+
+ public static String getIdLabel(ObjectReference objRef) {
+ StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
+ final boolean showConcreteType =
+ !classRenderer.SHOW_DECLARED_TYPE ||
+ (!(objRef instanceof StringReference) && !(objRef instanceof ClassObjectReference) && !isEnumConstant(objRef));
+ if (showConcreteType || classRenderer.SHOW_OBJECT_ID) {
+ buf.append('{');
+ if (showConcreteType) {
+ buf.append(classRenderer.renderTypeName(objRef.type().name()));
+ }
+ if (classRenderer.SHOW_OBJECT_ID) {
+ buf.append('@');
+ if(ApplicationManager.getApplication().isUnitTestMode()) {
+ //noinspection HardCodedStringLiteral
+ buf.append("uniqueID");
+ }
+ else {
+ buf.append(objRef.uniqueID());
+ }
+ }
+ buf.append('}');
+ }
+
+ if (objRef instanceof ArrayReference) {
+ int idx = buf.indexOf("[");
+ if(idx >= 0) {
+ buf.insert(idx + 1, Integer.toString(((ArrayReference)objRef).length()));
+ }
+ }
+
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ private static boolean isEnumConstant(final ObjectReference objRef) {
+ final Type type = objRef.type();
+ return type instanceof ClassType && ((ClassType)type).isEnum();
+ }
+
+ public boolean canSetValue() {
+ return !myIsSynthetic && isLvalue();
+ }
+
+ public String getValueLabel() {
+ return myValueLabel;
+ }
+
+ //Context is set to null
+ public void clear() {
+ super.clear();
+ setValueLabel("");
+ myIsExpandable = false;
+ }
+
+ @Nullable
+ public ValueMarkup getMarkup(final DebugProcess debugProcess) {
+ final Value value = getValue();
+ if (value instanceof ObjectReference) {
+ final ObjectReference objRef = (ObjectReference)value;
+ final Map<ObjectReference, ValueMarkup> map = getMarkupMap(debugProcess);
+ if (map != null) {
+ return map.get(objRef);
+ }
+ }
+ return null;
+ }
+
+ public void setMarkup(final DebugProcess debugProcess, @Nullable final ValueMarkup markup) {
+ final Value value = getValue();
+ if (value instanceof ObjectReference) {
+ final Map<ObjectReference, ValueMarkup> map = getMarkupMap(debugProcess);
+ if (map != null) {
+ final ObjectReference objRef = (ObjectReference)value;
+ if (markup != null) {
+ map.put(objRef, markup);
+ }
+ else {
+ map.remove(objRef);
+ }
+ }
+ }
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java
new file mode 100644
index 0000000..eaeab2d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java
@@ -0,0 +1,87 @@
+/*
+ * 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 WatchItemDescriptor
+ * @author Jeka
+ */
+package com.intellij.debugger.ui.impl.watch;
+
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.psi.PsiCodeFragment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * update(Value, boolean) method must be called whenever the state of the target VM changes
+ */
+public class WatchItemDescriptor extends EvaluationDescriptor {
+
+ @Nullable
+ private String myCustomName = null;
+
+ public WatchItemDescriptor(Project project, TextWithImports text) {
+ super(text, project);
+ setValueLabel("");
+ }
+
+ public WatchItemDescriptor(Project project, TextWithImports text, Value value) {
+ super(text, project, value);
+ setValueLabel("");
+ }
+
+ public String getName() {
+ final String customName = myCustomName;
+ return customName == null? getEvaluationText().getText() : customName;
+ }
+
+ public void setCustomName(@Nullable String customName) {
+ myCustomName = customName;
+ }
+
+ public void setNew() {
+ myIsNew = true;
+ }
+
+ // call update() after setting a new expression
+ public void setEvaluationText(TextWithImports evaluationText) {
+ if (!Comparing.equal(getEvaluationText(), evaluationText)) {
+ setLvalue(false);
+ }
+ myText = evaluationText;
+ myIsNew = true;
+ setValueLabel("");
+ }
+
+ protected EvaluationContextImpl getEvaluationContext(EvaluationContextImpl evaluationContext) {
+ return evaluationContext;
+ }
+
+ protected PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException {
+ final PsiElement psiContext = PositionUtil.getContextElement(context);
+ final PsiCodeFragment fragment = getEffectiveCodeFragmentFactory(psiContext).createCodeFragment(getEvaluationText(), psiContext, myProject);
+ fragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
+ return fragment;
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/ArrayElementDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ArrayElementDescriptor.java
new file mode 100644
index 0000000..a2a990d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ArrayElementDescriptor.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.sun.jdi.ArrayReference;
+
+public interface ArrayElementDescriptor extends NodeDescriptor{
+ ArrayReference getArray();
+ int getIndex();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/DebuggerTreeNode.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/DebuggerTreeNode.java
new file mode 100644
index 0000000..0825eb2
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/DebuggerTreeNode.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+
+import javax.swing.tree.MutableTreeNode;
+
+public interface DebuggerTreeNode extends MutableTreeNode{
+ DebuggerTreeNode getParent();
+
+ NodeDescriptor getDescriptor();
+
+ Project getProject();
+
+ void setRenderer(NodeRenderer renderer);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/FieldDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/FieldDescriptor.java
new file mode 100644
index 0000000..4067b87
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/FieldDescriptor.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.sun.jdi.Field;
+import com.sun.jdi.ObjectReference;
+
+public interface FieldDescriptor extends NodeDescriptor{
+ Field getField();
+ ObjectReference getObject();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/LocalVariableDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/LocalVariableDescriptor.java
new file mode 100644
index 0000000..a5f90f4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/LocalVariableDescriptor.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.jdi.LocalVariableProxy;
+
+public interface LocalVariableDescriptor extends ValueDescriptor{
+ LocalVariableProxy getLocalVariable();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/NodeDescriptorFactory.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/NodeDescriptorFactory.java
new file mode 100644
index 0000000..18eb67d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/NodeDescriptorFactory.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.jdi.LocalVariableProxy;
+import com.intellij.debugger.ui.impl.watch.UserExpressionDescriptorImpl;
+import com.intellij.debugger.impl.descriptors.data.DescriptorData;
+import com.sun.jdi.ArrayReference;
+import com.sun.jdi.Field;
+import com.sun.jdi.ObjectReference;
+
+/**
+ * creates descriptors
+ * if descriptor was already created in current context (that is location in debugee code) returns that descriptor
+ * else creates new descriptor and restores it's representation properties from history
+ */
+
+public interface NodeDescriptorFactory {
+ ArrayElementDescriptor getArrayItemDescriptor(NodeDescriptor parent, ArrayReference array, int index);
+
+ FieldDescriptor getFieldDescriptor(NodeDescriptor parent, ObjectReference objRef, Field field);
+
+ LocalVariableDescriptor getLocalVariableDescriptor(NodeDescriptor parent, LocalVariableProxy local);
+
+ UserExpressionDescriptor getUserExpressionDescriptor(NodeDescriptor parent, final DescriptorData<UserExpressionDescriptor> data);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/NodeManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/NodeManager.java
new file mode 100644
index 0000000..ff612fe
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/NodeManager.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+
+public interface NodeManager {
+ DebuggerTreeNode createMessageNode(String s);
+
+ DebuggerTreeNode createNode(NodeDescriptor nodeDescriptor, EvaluationContext evaluationContext);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/StackFrameDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/StackFrameDescriptor.java
new file mode 100644
index 0000000..7712b15
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/StackFrameDescriptor.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.StackFrameContext;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+
+public interface StackFrameDescriptor extends NodeDescriptor, StackFrameContext {
+ @Nullable
+ Color getBackgroundColor();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/StaticDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/StaticDescriptor.java
new file mode 100644
index 0000000..ccd83ad
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/StaticDescriptor.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.sun.jdi.ReferenceType;
+
+public interface StaticDescriptor extends NodeDescriptor{
+ ReferenceType getType();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/ThreadDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ThreadDescriptor.java
new file mode 100644
index 0000000..f4b564d
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ThreadDescriptor.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
+
+public interface ThreadDescriptor extends NodeDescriptor{
+ public ThreadReferenceProxy getThreadReference();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/ThreadGroupDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ThreadGroupDescriptor.java
new file mode 100644
index 0000000..89ba3ce
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ThreadGroupDescriptor.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.jdi.ThreadGroupReferenceProxy;
+
+public interface ThreadGroupDescriptor extends NodeDescriptor{
+ ThreadGroupReferenceProxy getThreadGroupReference();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/UserExpressionDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/UserExpressionDescriptor.java
new file mode 100644
index 0000000..5641d82
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/UserExpressionDescriptor.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+
+public interface UserExpressionDescriptor extends ValueDescriptor {
+ public void setContext(EvaluationContextImpl evaluationContext);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/ValueDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ValueDescriptor.java
new file mode 100644
index 0000000..d4a444e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/ValueDescriptor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.psi.PsiExpression;
+import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public interface ValueDescriptor extends NodeDescriptor{
+ PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException;
+
+ Value getValue();
+
+ String setValueLabel(String label);
+
+ String setValueLabelFailed(EvaluateException e);
+
+ Icon setValueIcon(Icon icon);
+
+ boolean isArray();
+ boolean isLvalue();
+ boolean isNull();
+ boolean isPrimitive();
+ boolean isString();
+
+ @Nullable
+ ValueMarkup getMarkup(final DebugProcess debugProcess);
+
+ void setMarkup(final DebugProcess debugProcess, @Nullable ValueMarkup markup);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAs.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAs.java
new file mode 100644
index 0000000..9e713a3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAs.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.actions;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.SuspendContext;
+import com.intellij.debugger.engine.managerThread.SuspendContextCommand;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.sun.jdi.*;
+
+import java.util.Enumeration;
+
+public class ShowAllAs extends AnAction {
+ private final NodeRenderer myRenderer;
+
+ public ShowAllAs(NodeRenderer renderer) {
+ myRenderer = renderer;
+ }
+
+ private boolean isPrimitiveArray(DebuggerTreeNode selectedNode) {
+ try {
+ if(selectedNode.getDescriptor() instanceof ValueDescriptor) {
+ ValueDescriptor valueDescriptor = ((ValueDescriptor)selectedNode.getDescriptor());
+ if(valueDescriptor.isArray()) {
+ ArrayReference arrayReference = ((ArrayReference)valueDescriptor.getValue());
+ Type componentType = ((ArrayType)arrayReference.type()).componentType();
+ if(componentType instanceof PrimitiveType) {
+ if(componentType instanceof ByteType ||
+ componentType instanceof ShortType ||
+ componentType instanceof IntegerType ||
+ componentType instanceof LongType) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ catch (ClassNotLoadedException e) {
+ }
+ return false;
+ }
+
+ public void update(AnActionEvent e) {
+ DebuggerTreeNode selectedNode = ((DebuggerUtilsEx)DebuggerUtils.getInstance()).getSelectedNode(e.getDataContext());
+ e.getPresentation().setVisible(myRenderer != null && selectedNode != null && isPrimitiveArray(selectedNode));
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ DebuggerTreeNode selectedNode = ((DebuggerUtilsEx)DebuggerUtils.getInstance()).getSelectedNode(e.getDataContext());
+ if(selectedNode == null) return;
+
+ if(!isPrimitiveArray(selectedNode)) return;
+
+ final DebuggerContext debuggerContext = DebuggerUtils.getInstance().getDebuggerContext(e.getDataContext());
+ if(debuggerContext == null || debuggerContext.getDebugProcess() == null) return;
+
+ for(Enumeration children = selectedNode.children(); children.hasMoreElements(); ) {
+ final DebuggerTreeNode child = (DebuggerTreeNode)children.nextElement();
+ if(child.getDescriptor() instanceof ValueDescriptor) {
+ debuggerContext.getDebugProcess().getManagerThread().invokeCommand(new SuspendContextCommand() {
+ public SuspendContext getSuspendContext() {
+ return debuggerContext.getSuspendContext();
+ }
+
+ public void action() {
+ child.setRenderer(myRenderer);
+ }
+
+ public void commandCancelled() {
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAsDecimal.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAsDecimal.java
new file mode 100644
index 0000000..9fd2f90
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAsDecimal.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.actions;
+
+import com.intellij.debugger.settings.NodeRendererSettings;
+
+
+public class ShowAllAsDecimal extends ShowAllAs{
+ public ShowAllAsDecimal() {
+ super(NodeRendererSettings.getInstance().getPrimitiveRenderer());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAsHex.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAsHex.java
new file mode 100644
index 0000000..cbf9376
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/actions/ShowAllAsHex.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.actions;
+
+import com.intellij.debugger.ui.tree.render.HexRenderer;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.debugger.settings.NodeRendererSettings;
+
+import java.util.List;
+import java.util.Iterator;
+
+public class ShowAllAsHex extends ShowAllAs {
+
+ public ShowAllAsHex() {
+ super(NodeRendererSettings.getInstance().getHexRenderer());
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java
new file mode 100644
index 0000000..54a2c60
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.settings.ViewsGeneralSettings;
+import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
+import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.NodeDescriptorFactory;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.ArrayReference;
+import com.sun.jdi.ArrayType;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * User: lex
+ * Date: Sep 18, 2003
+ * Time: 3:07:19 PM
+ */
+public class ArrayRenderer extends NodeRendererImpl{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.ArrayRenderer");
+
+ public static final @NonNls String UNIQUE_ID = "ArrayRenderer";
+
+ public int START_INDEX = 0;
+ public int END_INDEX = 100;
+ public int ENTRIES_LIMIT = 101;
+ private final static String MORE_ELEMENTS = "...";
+
+ public ArrayRenderer() {
+ myProperties.setEnabled(true);
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public boolean isEnabled() {
+ return myProperties.isEnabled();
+ }
+
+ public void setEnabled(boolean enabled) {
+ myProperties.setEnabled(enabled);
+ }
+
+ public @NonNls String getName() {
+ return "Array";
+ }
+
+ public void setName(String text) {
+ LOG.assertTrue(false);
+ }
+
+ public ArrayRenderer clone() {
+ return (ArrayRenderer)super.clone();
+ }
+
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ return ClassRenderer.calcLabel(descriptor);
+ }
+
+ public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ List<DebuggerTreeNode> children = new ArrayList<DebuggerTreeNode>();
+ NodeManagerImpl nodeManager = (NodeManagerImpl)builder.getNodeManager();
+ NodeDescriptorFactory descriptorFactory = builder.getDescriptorManager();
+
+ ArrayReference array = (ArrayReference)value;
+ if (array.length() > 0) {
+ int added = 0;
+
+ if(ENTRIES_LIMIT > END_INDEX - START_INDEX + 1) {
+ ENTRIES_LIMIT = END_INDEX - START_INDEX;
+ }
+
+ if(ENTRIES_LIMIT <= 0) {
+ ENTRIES_LIMIT = 1;
+ }
+
+ if(array.length() - 1 >= START_INDEX) {
+ int start = START_INDEX;
+ int end = array.length() - 1 < END_INDEX ? array.length() - 1 : END_INDEX;
+
+ int idx;
+
+ for (idx = start; idx <= end; idx++) {
+ DebuggerTreeNode arrayItemNode = nodeManager.createNode(descriptorFactory.getArrayItemDescriptor(builder.getParentDescriptor(), array, idx), evaluationContext);
+
+ if (ViewsGeneralSettings.getInstance().HIDE_NULL_ARRAY_ELEMENTS && ((ValueDescriptorImpl)arrayItemNode.getDescriptor()).isNull()) continue;
+ if(added >= (ENTRIES_LIMIT + 1)/ 2) break;
+ children.add(arrayItemNode);
+ added++;
+ }
+
+ start = idx;
+
+ List<DebuggerTreeNode> childrenTail = new ArrayList<DebuggerTreeNode>();
+ for (idx = end; idx >= start; idx--) {
+ DebuggerTreeNode arrayItemNode = nodeManager.createNode(descriptorFactory.getArrayItemDescriptor(builder.getParentDescriptor(), array, idx), evaluationContext);
+
+ if (ViewsGeneralSettings.getInstance().HIDE_NULL_ARRAY_ELEMENTS && ((ValueDescriptorImpl)arrayItemNode.getDescriptor()).isNull()) continue;
+ if(added >= ENTRIES_LIMIT) break;
+ childrenTail.add(arrayItemNode);
+ added++;
+ }
+
+ //array is printed in the following way
+ // ...
+ // items1...itemENTRIES_LIMIT/2
+ // ...
+ // itemENTRIES_LIMIT/2+1...itemENTRIES_LIMIT
+ // ...
+
+ //when itemENTRIES_LIMIT/2+1...itemENTRIES_LIMIT set is empty, we should not add middle "..." node
+ if(idx >= start && !(ENTRIES_LIMIT == 1 && END_INDEX < array.length())) {
+ children.add(nodeManager.createMessageNode(new MessageDescriptor(MORE_ELEMENTS, MessageDescriptor.SPECIAL)));
+ }
+
+ for (ListIterator<DebuggerTreeNode> iterator = childrenTail.listIterator(childrenTail.size()); iterator.hasPrevious();) {
+ DebuggerTreeNode debuggerTreeNode = iterator.previous();
+ children.add(debuggerTreeNode);
+ }
+ }
+
+ if (added == 0) {
+ if(START_INDEX == 0 && array.length() - 1 <= END_INDEX) {
+ children.add(nodeManager.createMessageNode(MessageDescriptor.ALL_ELEMENTS_IN_RANGE_ARE_NULL.getLabel()));
+ }
+ else {
+ children.add(nodeManager.createMessageNode(MessageDescriptor.ALL_ELEMENTS_IN_VISIBLE_RANGE_ARE_NULL.getLabel()));
+ }
+ }
+ else {
+ if(START_INDEX > 0) {
+ children.add(0, nodeManager.createMessageNode(new MessageDescriptor(MORE_ELEMENTS, MessageDescriptor.SPECIAL)));
+ }
+
+ if(END_INDEX < array.length() - 1) {
+ children.add(nodeManager.createMessageNode(new MessageDescriptor(MORE_ELEMENTS, MessageDescriptor.SPECIAL)));
+ }
+ }
+ }
+ builder.setChildren(children);
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+ DefaultJDOMExternalizer.readExternal(this, element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) {
+ LOG.assertTrue(node.getDescriptor() instanceof ArrayElementDescriptorImpl, node.getDescriptor().getClass().getName());
+ ArrayElementDescriptorImpl descriptor = (ArrayElementDescriptorImpl)node.getDescriptor();
+
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(node.getProject()).getElementFactory();
+ try {
+ LanguageLevel languageLevel = LanguageLevelProjectExtension.getInstance(node.getProject()).getLanguageLevel();
+ return elementFactory.createExpressionFromText("this[" + descriptor.getIndex() + "]", elementFactory.getArrayClass(languageLevel));
+ }
+ catch (IncorrectOperationException e) {
+ LOG.error(e);
+ return null;
+ }
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ return value instanceof ArrayReference && ((ArrayReference)value).length() > 0;
+ }
+
+ public boolean isApplicable(Type type) {
+ return (type instanceof ArrayType);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BasicRendererProperties.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BasicRendererProperties.java
new file mode 100644
index 0000000..d00e601
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BasicRendererProperties.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 12, 2005
+ */
+public final class BasicRendererProperties implements Cloneable, JDOMExternalizable{
+ // todo: add class filters here
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.BasicRendererProperties");
+
+ private static final @NonNls String NAME_OPTION = "NAME";
+ private String myName;
+
+ private static final @NonNls String ENABLED_OPTION = "ENABLED";
+ private Boolean myEnabled;
+
+ private static final @NonNls String CLASSNAME_OPTION = "QUALIFIED_NAME";
+ private String myClassName;
+
+ public String getName() {
+ return myName;
+ }
+
+ public void setName(final String name) {
+ myName = name;
+ }
+
+ public boolean isEnabled() {
+ return myEnabled != null? myEnabled.booleanValue() : false;
+ }
+
+ public void setEnabled(final boolean enabled) {
+ myEnabled = enabled? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public String getClassName() {
+ return myClassName;
+ }
+
+ public void setClassName(final String className) {
+ myClassName = className;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) public void readExternal(Element element) throws InvalidDataException {
+ final List options = element.getChildren("option");
+ myName = null;
+ myEnabled = null;
+ myClassName = null;
+ for (Iterator it = options.iterator(); it.hasNext();) {
+ final Element option = (Element)it.next();
+ final String optionName = option.getAttributeValue("name");
+ if (NAME_OPTION.equals(optionName)) {
+ myName = option.getAttributeValue("value");
+ }
+ else if (ENABLED_OPTION.equals(optionName)) {
+ final String val = option.getAttributeValue("value");
+ myEnabled = "true".equalsIgnoreCase(val)? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if (CLASSNAME_OPTION.equals(optionName)) {
+ myClassName = option.getAttributeValue("value");
+ }
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) public void writeExternal(Element element) throws WriteExternalException {
+ if (myName != null) {
+ addOption(element, NAME_OPTION, myName);
+ }
+ if (myEnabled != null) {
+ addOption(element, ENABLED_OPTION, myEnabled.booleanValue()? "true" : "false");
+ }
+ if (myClassName != null) {
+ addOption(element, CLASSNAME_OPTION, myClassName);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private void addOption(final Element element, final String optionName, final String optionValue) {
+ final Element option = new Element("option");
+ element.addContent(option);
+ option.setAttribute("name", optionName);
+ option.setAttribute("value", optionValue);
+ }
+
+ public BasicRendererProperties clone() {
+ try {
+ return (BasicRendererProperties)super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ LOG.error(e);
+ }
+ return null;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java
new file mode 100644
index 0000000..abf937e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerManager;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessAdapter;
+import com.intellij.debugger.engine.SuspendContext;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
+import com.intellij.debugger.engine.managerThread.SuspendContextCommand;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Key;
+import com.intellij.rt.debugger.BatchEvaluatorServer;
+import com.intellij.util.containers.HashMap;
+import com.sun.jdi.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Jul 7, 2003
+ * Time: 11:13:52 PM
+ */
+
+public class BatchEvaluator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.BatchEvaluator");
+
+ private final DebugProcess myDebugProcess;
+ private boolean myBatchEvaluatorChecked;
+ private ObjectReference myBatchEvaluatorObject;
+ private Method myBatchEvaluatorMethod;
+
+ private static final Key<BatchEvaluator> BATCH_EVALUATOR_KEY = new Key<BatchEvaluator>("BatchEvaluator");
+ public static final Key<Boolean> REMOTE_SESSION_KEY = new Key<Boolean>("is_remote_session_key");
+
+ private final HashMap<SuspendContext, List<ToStringCommand>> myBuffer = new HashMap<SuspendContext, List<ToStringCommand>>();
+
+ private BatchEvaluator(DebugProcess process) {
+ myDebugProcess = process;
+ myDebugProcess.addDebugProcessListener(new DebugProcessAdapter() {
+ public void processDetached(DebugProcess process, boolean closedByUser) {
+ myBatchEvaluatorChecked = false;
+ myBatchEvaluatorObject= null;
+ myBatchEvaluatorMethod = null;
+ }
+ });
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"}) public boolean hasBatchEvaluator(EvaluationContext evaluationContext) {
+ if (!myBatchEvaluatorChecked) {
+ myBatchEvaluatorChecked = true;
+ final Boolean isRemote = myDebugProcess.getUserData(REMOTE_SESSION_KEY);
+ if (isRemote != null && isRemote.booleanValue()) {
+ // optimization: for remote sessions the BatchEvaluator is not there for sure
+ return false;
+ }
+
+ ThreadReferenceProxy thread = evaluationContext.getSuspendContext().getThread();
+
+ if (thread == null) {
+ return false;
+ }
+
+ ThreadReference threadReference = thread.getThreadReference();
+ if(threadReference == null) {
+ return false;
+ }
+
+ ClassType batchEvaluatorClass = null;
+ try {
+ batchEvaluatorClass = (ClassType)myDebugProcess.findClass(evaluationContext, BatchEvaluatorServer.class.getName(),
+ evaluationContext.getClassLoader());
+ }
+ catch (EvaluateException e) {
+ }
+
+ if (batchEvaluatorClass != null) {
+ Method constructor = batchEvaluatorClass.concreteMethodByName("<init>", "()V");
+ if(constructor != null){
+ ObjectReference evaluator = null;
+ try {
+ evaluator = myDebugProcess.newInstance(evaluationContext, batchEvaluatorClass, constructor, new ArrayList());
+ }
+ catch (Exception e) {
+ LOG.debug(e);
+ }
+ myBatchEvaluatorObject = evaluator;
+
+ if(myBatchEvaluatorObject != null) {
+ myBatchEvaluatorMethod = batchEvaluatorClass.concreteMethodByName("evaluate", "([Ljava/lang/Object;)[Ljava/lang/Object;");
+ }
+ }
+ }
+ }
+ return myBatchEvaluatorMethod != null;
+ }
+
+ public void invoke(ToStringCommand command) {
+ LOG.assertTrue(DebuggerManager.getInstance(myDebugProcess.getProject()).isDebuggerManagerThread());
+
+ final EvaluationContext evaluationContext = command.getEvaluationContext();
+ final SuspendContext suspendContext = evaluationContext.getSuspendContext();
+
+ if(!hasBatchEvaluator(evaluationContext)) {
+ myDebugProcess.getManagerThread().invokeCommand(command);
+ }
+ else {
+ List<ToStringCommand> toStringCommands = myBuffer.get(suspendContext);
+ if(toStringCommands == null) {
+ final List<ToStringCommand> commands = new ArrayList<ToStringCommand>();
+ toStringCommands = commands;
+ myBuffer.put(suspendContext, commands);
+
+ myDebugProcess.getManagerThread().invokeCommand(new SuspendContextCommand() {
+ public SuspendContext getSuspendContext() {
+ return suspendContext;
+ }
+
+ public void action() {
+ myBuffer.remove(suspendContext);
+
+ if(!doEvaluateBatch(commands, evaluationContext)) {
+ for (Iterator<ToStringCommand> iterator = commands.iterator(); iterator.hasNext();) {
+ ToStringCommand toStringCommand = iterator.next();
+ toStringCommand.action();
+ }
+ }
+ }
+
+ public void commandCancelled() {
+ myBuffer.remove(suspendContext);
+ }
+ });
+ }
+
+ toStringCommands.add(command);
+ }
+ }
+
+ public static BatchEvaluator getBatchEvaluator(DebugProcess debugProcess) {
+ BatchEvaluator batchEvaluator = debugProcess.getUserData(BATCH_EVALUATOR_KEY);
+
+ if(batchEvaluator == null) {
+ batchEvaluator = new BatchEvaluator(debugProcess);
+ debugProcess.putUserData(BATCH_EVALUATOR_KEY, batchEvaluator);
+ }
+ return batchEvaluator;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private boolean doEvaluateBatch(List<ToStringCommand> requests, EvaluationContext evaluationContext) {
+ try {
+ DebugProcess debugProcess = evaluationContext.getDebugProcess();
+ List<Value> values = new ArrayList<Value>();
+ for (Iterator<ToStringCommand> iterator = requests.iterator(); iterator.hasNext();) {
+ ToStringCommand toStringCommand = iterator.next();
+ final Value value = toStringCommand.getValue();
+ values.add(value instanceof ObjectReference? ((ObjectReference)value) : value);
+ }
+
+ ArrayType objectArrayClass = (ArrayType)debugProcess.findClass(
+ evaluationContext,
+ "java.lang.Object[]",
+ evaluationContext.getClassLoader());
+ if (objectArrayClass == null) {
+ return false;
+ }
+
+ ArrayReference argArray = debugProcess.newInstance(objectArrayClass, values.size());
+ ((SuspendContextImpl)evaluationContext.getSuspendContext()).keep(argArray); // to avoid ObjectCollectedException
+ argArray.setValues(values);
+ List argList = new ArrayList(1);
+ argList.add(argArray);
+ Value value = debugProcess.invokeMethod(evaluationContext, myBatchEvaluatorObject,
+ myBatchEvaluatorMethod, argList);
+ if (value instanceof ArrayReference) {
+ ((SuspendContextImpl)evaluationContext.getSuspendContext()).keep((ArrayReference)value); // to avoid ObjectCollectedException for both the array and its elements
+ final ArrayReference strings = (ArrayReference)value;
+ final List<Value> allValuesArray = strings.getValues();
+ final Value[] allValues = allValuesArray.toArray(new Value[allValuesArray.size()]);
+ int idx = 0;
+ for (Iterator<ToStringCommand> iterator = requests.iterator(); iterator.hasNext(); idx++) {
+ ToStringCommand request = iterator.next();
+ final Value strValue = allValues[idx];
+ if(strValue == null || strValue instanceof StringReference){
+ try {
+ String str = (strValue == null)? null : ((StringReference)strValue).value();
+ request.evaluationResult(str);
+ }
+ catch (ObjectCollectedException e) {
+ // ignored
+ }
+ }
+ else if(strValue instanceof ObjectReference){
+ request.evaluationError(EvaluateExceptionUtil.createEvaluateException(new InvocationException((ObjectReference)strValue)).getMessage());
+ }
+ else {
+ LOG.assertTrue(false);
+ }
+ request.setEvaluated();
+ }
+ }
+ return true;
+ }
+ catch (ClassNotLoadedException e) {
+ }
+ catch (InvalidTypeException e) {
+ }
+ catch (EvaluateException e) {
+ }
+ catch (ObjectCollectedException e) {
+ }
+ return false;
+ }
+
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CachedEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CachedEvaluator.java
new file mode 100644
index 0000000..2861e2a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CachedEvaluator.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.reference.SoftReference;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Dec 27, 2003
+ * Time: 7:56:13 PM
+ * To change this template use Options | File Templates.
+ */
+public abstract class CachedEvaluator {
+ private final CodeFragmentFactory myDefaultFragmentFactory;
+
+ public CachedEvaluator() {
+ myDefaultFragmentFactory = new CodeFragmentFactoryContextWrapper(DefaultCodeFragmentFactory.getInstance());
+ }
+
+ private static class Cache {
+ protected ExpressionEvaluator myEvaluator;
+ protected EvaluateException myException;
+ protected PsiExpression myPsiChildrenExpression;
+ }
+
+ SoftReference<Cache> myCache = new SoftReference<Cache>(null);
+ private TextWithImports myReferenceExpression;
+
+ protected abstract String getClassName();
+
+ public TextWithImports getReferenceExpression() {
+ return myReferenceExpression != null ? myReferenceExpression : DebuggerUtils.getInstance().createExpressionWithImports("");
+ }
+
+ public void setReferenceExpression(TextWithImports referenceExpression) {
+ myReferenceExpression = referenceExpression;
+ clear();
+ }
+
+ public void clear() {
+ myCache.clear();
+ }
+
+ protected Cache initEvaluatorAndChildrenExpression(final Project project) {
+ final Cache cache = new Cache();
+ try {
+ final PsiClass contextClass = DebuggerUtils.findClass(getClassName(), project, GlobalSearchScope.allScope(project));
+ if(contextClass == null) {
+ throw EvaluateExceptionUtil.CANNOT_FIND_SOURCE_CLASS;
+ }
+ final PsiType contextType = DebuggerUtils.getType(getClassName(), project);
+ cache.myPsiChildrenExpression = null;
+ JavaCodeFragment codeFragment = myDefaultFragmentFactory.createCodeFragment(myReferenceExpression, contextClass, project);
+ codeFragment.forceResolveScope(GlobalSearchScope.allScope(project));
+ codeFragment.setThisType(contextType);
+ DebuggerUtils.checkSyntax(codeFragment);
+ cache.myPsiChildrenExpression = ((PsiExpressionCodeFragment)codeFragment).getExpression();
+ cache.myEvaluator = myDefaultFragmentFactory.getEvaluatorBuilder().build(cache.myPsiChildrenExpression, null);
+ }
+ catch (EvaluateException e) {
+ cache.myException = e;
+ }
+
+ myCache = new SoftReference<Cache>(cache);
+ return cache;
+ }
+
+ protected ExpressionEvaluator getEvaluator(final Project project) throws EvaluateException {
+ Cache cache = myCache.get();
+ if(cache == null) {
+ cache = PsiDocumentManager.getInstance(project).commitAndRunReadAction(new Computable<Cache>() {
+ public Cache compute() {
+ return initEvaluatorAndChildrenExpression(project);
+ }
+ });
+ }
+
+ if(cache.myException != null) {
+ throw cache.myException;
+ }
+
+ return cache.myEvaluator;
+ }
+
+ protected PsiExpression getPsiExpression(final Project project) {
+ Cache cache = myCache.get();
+ if(cache == null) {
+ cache = initEvaluatorAndChildrenExpression(project);
+ }
+
+ return cache.myPsiChildrenExpression;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java
new file mode 100644
index 0000000..8a1f358
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.ui.tree.*;
+
+import java.util.List;
+
+public interface ChildrenBuilder {
+ NodeDescriptorFactory getDescriptorManager();
+
+ NodeManager getNodeManager();
+
+ ValueDescriptor getParentDescriptor();
+
+ void setChildren(List<DebuggerTreeNode> children);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java
new file mode 100644
index 0000000..969635a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.Value;
+
+public interface ChildrenRenderer extends Renderer {
+ void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext);
+
+ /**
+ * - parentNode
+ * + ..
+ * + node
+ * + ...
+ *
+ * is invoked on the renderer of the parentNode
+ * @param node a child node
+ * @return expression that evaluates the child node.
+ * Use 'this' to refer the expression that evaluates this (parent) node
+ * @param context
+ */
+ PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException;
+
+ boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java
new file mode 100644
index 0000000..bdf89a5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
+import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.sun.jdi.*;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 17, 2003
+ * Time: 2:04:00 PM
+ */
+public class ClassRenderer extends NodeRendererImpl{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.ClassRenderer");
+
+ public static final @NonNls String UNIQUE_ID = "ClassRenderer";
+
+ public boolean SORT_ASCENDING = false;
+ public boolean SHOW_SYNTHETICS = true;
+ public boolean SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES = true;
+ public boolean SHOW_STATIC = false;
+ public boolean SHOW_STATIC_FINAL = false;
+
+ public boolean SHOW_FQ_TYPE_NAMES = true;
+ public boolean SHOW_DECLARED_TYPE = false;
+ public boolean SHOW_OBJECT_ID = true;
+
+ public ClassRenderer() {
+ myProperties.setEnabled(true);
+ }
+
+ public final String renderTypeName(final String typeName) {
+ if (SHOW_FQ_TYPE_NAMES) {
+ return typeName;
+ }
+ final int dotIndex = typeName.lastIndexOf('.');
+ if (dotIndex > 0) {
+ return typeName.substring(dotIndex + 1);
+ }
+ return typeName;
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public boolean isEnabled() {
+ return myProperties.isEnabled();
+ }
+
+ public void setEnabled(boolean enabled) {
+ myProperties.setEnabled(enabled);
+ }
+
+ public ClassRenderer clone() {
+ return (ClassRenderer) super.clone();
+ }
+
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener) throws EvaluateException {
+ return calcLabel(descriptor);
+ }
+
+ protected static String calcLabel(ValueDescriptor descriptor) {
+ final ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
+ final Value value = valueDescriptor.getValue();
+ if (value instanceof ObjectReference) {
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ if (value instanceof StringReference) {
+ buf.append('\"');
+ buf.append(DebuggerUtils.convertToPresentationString(((StringReference)value).value()));
+ buf.append('\"');
+ }
+ else if (value instanceof ClassObjectReference) {
+ ReferenceType type = ((ClassObjectReference)value).reflectedType();
+ buf.append((type != null)?type.name():"{...}");
+ }
+ else {
+ final ObjectReference objRef = (ObjectReference)value;
+ final Type type = objRef.type();
+ if (type instanceof ClassType && ((ClassType)type).isEnum()) {
+ final String name = getEnumConstantName(objRef, (ClassType)type);
+ if (name != null) {
+ buf.append(name);
+ }
+ else {
+ buf.append(type.name());
+ }
+ }
+ else {
+ buf.append(ValueDescriptorImpl.getIdLabel(objRef));
+ }
+ }
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+ else if(value == null) {
+ //noinspection HardCodedStringLiteral
+ return "null";
+ }
+ else {
+ return DebuggerBundle.message("label.undefined");
+ }
+ }
+
+ public void buildChildren(final Value value, final ChildrenBuilder builder, final EvaluationContext evaluationContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ final ValueDescriptorImpl parentDescriptor = (ValueDescriptorImpl)builder.getParentDescriptor();
+ final NodeManager nodeManager = builder.getNodeManager();
+ final NodeDescriptorFactory nodeDescriptorFactory = builder.getDescriptorManager();
+
+ List<DebuggerTreeNode> children = new ArrayList<DebuggerTreeNode>();
+ if (value instanceof ObjectReference) {
+ final ObjectReference objRef = (ObjectReference)value;
+ final ReferenceType refType = objRef.referenceType();
+ // default ObjectReference processing
+ final List<Field> fields = refType.allFields();
+ if (fields.size() > 0) {
+ for (final Field field : fields) {
+ if (!shouldDisplay(evaluationContext, objRef, field)) {
+ continue;
+ }
+ children.add(nodeManager.createNode(nodeDescriptorFactory.getFieldDescriptor(parentDescriptor, objRef, field), evaluationContext));
+ }
+
+ if(SORT_ASCENDING) {
+ Collections.sort(children, NodeManagerImpl.getNodeComparator());
+ }
+ }
+ else {
+ children.add(nodeManager.createMessageNode(MessageDescriptor.CLASS_HAS_NO_FIELDS.getLabel()));
+ }
+ }
+ builder.setChildren(children);
+ }
+
+ private boolean shouldDisplay(EvaluationContext context, @NotNull ObjectReference objInstance, @NotNull Field field) {
+ final boolean isSynthetic = DebuggerUtils.isSynthetic(field);
+ if (!SHOW_SYNTHETICS && isSynthetic) {
+ return false;
+ }
+ if (SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES && isSynthetic) {
+ try {
+ final StackFrameProxy frameProxy = context.getFrameProxy();
+ if (frameProxy != null) {
+ final Location location = frameProxy.location();
+ if (location != null && objInstance.equals(context.getThisObject()) && Comparing.equal(objInstance.referenceType(), location.declaringType()) && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) {
+ return false;
+ }
+ }
+ }
+ catch (EvaluateException ignored) {
+ }
+ }
+ if(!SHOW_STATIC && field.isStatic()) {
+ return false;
+ }
+
+ if(!SHOW_STATIC_FINAL && field.isStatic() && field.isFinal()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+ DefaultJDOMExternalizer.readExternal(this, element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
+ FieldDescriptor fieldDescriptor = (FieldDescriptor)node.getDescriptor();
+
+ PsiElementFactory elementFactory = JavaPsiFacade.getInstance(node.getProject()).getElementFactory();
+ try {
+ return elementFactory.createExpressionFromText(fieldDescriptor.getField().name(), DebuggerUtils.findClass(
+ fieldDescriptor.getObject().referenceType().name(), context.getProject(), context.getDebugProcess().getSearchScope())
+ );
+ }
+ catch (IncorrectOperationException e) {
+ throw new EvaluateException(DebuggerBundle.message("error.invalid.field.name", fieldDescriptor.getField().name()), null);
+ }
+ }
+
+ private static boolean valueExpandable(Value value) {
+ try {
+ if(value instanceof ArrayReference) {
+ return ((ArrayReference)value).length() > 0;
+ }
+ else if(value instanceof ObjectReference) {
+ return ((ObjectReference)value).referenceType().allFields().size() > 0;
+ }
+ }
+ catch (ObjectCollectedException e) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ return valueExpandable(value);
+ }
+
+ public boolean isApplicable(Type type) {
+ return type instanceof ReferenceType && !(type instanceof ArrayType);
+ }
+
+ public @NonNls String getName() {
+ return "Object";
+ }
+
+ public void setName(String text) {
+ LOG.assertTrue(false);
+ }
+
+ @Nullable
+ public static String getEnumConstantName(final ObjectReference objRef, ClassType classType) {
+ do {
+ if (!classType.isPrepared()) {
+ return null;
+ }
+ classType = classType.superclass();
+ if (classType == null) {
+ return null;
+ }
+ }
+ while (!("java.lang.Enum".equals(classType.name())));
+ //noinspection HardCodedStringLiteral
+ final Field field = classType.fieldByName("name");
+ if (field == null) {
+ return null;
+ }
+ final Value value = objRef.getValue(field);
+ if (!(value instanceof StringReference)) {
+ return null;
+ }
+ return ((StringReference)value).value();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundNodeConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundNodeConfigurable.java
new file mode 100644
index 0000000..21425b9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundNodeConfigurable.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class CompoundNodeConfigurable implements UnnamedConfigurable {
+ private final CompoundNodeRenderer myRenderer;
+
+ private final UnnamedConfigurable myLabelConfigurable;
+ private final UnnamedConfigurable myChildrenConfigurable;
+
+ private final static UnnamedConfigurable NULL_CONFIGURABLE = new UnnamedConfigurable() {
+ public JComponent createComponent() {
+ return new JPanel();
+ }
+
+ public boolean isModified() {
+ return false;
+ }
+
+ public void apply() {}
+ public void reset() {}
+ public void disposeUIResources() {}
+ };
+
+ public CompoundNodeConfigurable(CompoundNodeRenderer renderer,
+ UnnamedConfigurable labelConfigurable,
+ UnnamedConfigurable childrenConfigurable) {
+ myRenderer = renderer;
+ myLabelConfigurable = labelConfigurable != null ? labelConfigurable : NULL_CONFIGURABLE;
+ myChildrenConfigurable = childrenConfigurable != null ? childrenConfigurable : NULL_CONFIGURABLE;
+ }
+
+ public CompoundNodeRenderer getRenderer() {
+ return myRenderer;
+ }
+
+ public JComponent createComponent() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1.0;
+ c.insets = new Insets(0, 0, 5, 0);
+ c.gridwidth = GridBagConstraints.REMAINDER;
+
+ panel.add(myLabelConfigurable.createComponent(), c);
+
+ c.ipady = 1;
+ panel.add(new JSeparator(JSeparator.HORIZONTAL), c);
+
+ c.ipady = 0;
+ c.weighty = 1.0;
+ panel.add(myChildrenConfigurable.createComponent(), c);
+ return panel;
+ }
+
+ public boolean isModified() {
+ return myLabelConfigurable.isModified() || myChildrenConfigurable.isModified();
+ }
+
+ public void apply() throws ConfigurationException {
+ myLabelConfigurable.apply();
+ myChildrenConfigurable.apply();
+ }
+
+ public void reset() {
+ myLabelConfigurable.reset();
+ myChildrenConfigurable.reset();
+ }
+
+ public void disposeUIResources() {
+ myLabelConfigurable.disposeUIResources();
+ myChildrenConfigurable.disposeUIResources();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundNodeRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundNodeRenderer.java
new file mode 100644
index 0000000..99d11ff
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundNodeRenderer.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class CompoundNodeRenderer extends NodeRendererImpl{
+ public static final @NonNls String UNIQUE_ID = "CompoundNodeRenderer";
+
+ private ValueLabelRenderer myLabelRenderer;
+ private ChildrenRenderer myChildrenRenderer;
+ protected final NodeRendererSettings myRendererSettings;
+
+ public CompoundNodeRenderer(NodeRendererSettings rendererSettings, String name, ValueLabelRenderer labelRenderer, ChildrenRenderer childrenRenderer) {
+ myRendererSettings = rendererSettings;
+ setName(name);
+ myLabelRenderer = labelRenderer;
+ myChildrenRenderer = childrenRenderer;
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public CompoundNodeRenderer clone() {
+ CompoundNodeRenderer renderer = (CompoundNodeRenderer)super.clone();
+ renderer.myLabelRenderer = (myLabelRenderer != null) ? (ValueLabelRenderer)myLabelRenderer.clone() : null;
+ renderer.myChildrenRenderer = (myChildrenRenderer != null) ? (ChildrenRenderer)myChildrenRenderer.clone() : null;
+ return renderer;
+ }
+
+ public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
+ getChildrenRenderer().buildChildren(value, builder, evaluationContext);
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
+ return getChildrenRenderer().getChildValueExpression(node, context);
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ return getChildrenRenderer().isExpandable(value, evaluationContext, parentDescriptor);
+ }
+
+ public boolean isApplicable(Type type) {
+ return getLabelRenderer().isApplicable(type) && getChildrenRenderer().isApplicable(type);
+ }
+
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ return getLabelRenderer().calcLabel(descriptor, evaluationContext, listener);
+ }
+
+ public ValueLabelRenderer getLabelRenderer() {
+ return myLabelRenderer;
+ }
+
+ public ChildrenRenderer getChildrenRenderer() {
+ return myChildrenRenderer;
+ }
+
+ public void setLabelRenderer(ValueLabelRenderer labelRenderer) {
+ myLabelRenderer = labelRenderer;
+ }
+
+ public void setChildrenRenderer(ChildrenRenderer childrenRenderer) {
+ myChildrenRenderer = childrenRenderer;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+ final List children = element.getChildren(NodeRendererSettings.RENDERER_TAG);
+ if (children != null) {
+ for (Iterator it = children.iterator(); it.hasNext();) {
+ final Element elem = (Element)it.next();
+ final String role = elem.getAttributeValue("role");
+ if (role == null) {
+ continue;
+ }
+ if ("label".equals(role)) {
+ myLabelRenderer = (ValueLabelRenderer)myRendererSettings.readRenderer(elem);
+ }
+ else if ("children".equals(role)) {
+ myChildrenRenderer = (ChildrenRenderer)myRendererSettings.readRenderer(elem);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+ if (myLabelRenderer != null) {
+ final Element labelRendererElement = myRendererSettings.writeRenderer(myLabelRenderer);
+ labelRendererElement.setAttribute("role", "label");
+ element.addContent(labelRendererElement);
+ }
+ if (myChildrenRenderer != null) {
+ final Element childrenRendererElement = myRendererSettings.writeRenderer(myChildrenRenderer);
+ childrenRendererElement.setAttribute("role", "children");
+ element.addContent(childrenRendererElement);
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundReferenceRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundReferenceRenderer.java
new file mode 100644
index 0000000..c3e20f6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CompoundReferenceRenderer.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.CommonClassNames;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.Type;
+import org.jetbrains.annotations.NotNull;
+
+public class CompoundReferenceRenderer extends CompoundNodeRenderer{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.CompoundReferenceRenderer");
+
+ public CompoundReferenceRenderer(final NodeRendererSettings rendererSettings, String name, ValueLabelRenderer labelRenderer, ChildrenRenderer childrenRenderer) {
+ super(rendererSettings, name, labelRenderer, childrenRenderer);
+ myProperties.setClassName(CommonClassNames.JAVA_LANG_OBJECT);
+ LOG.assertTrue(labelRenderer == null || labelRenderer instanceof ReferenceRenderer);
+ LOG.assertTrue(childrenRenderer == null || childrenRenderer instanceof ReferenceRenderer);
+ }
+
+ public void setLabelRenderer(ValueLabelRenderer labelRenderer) {
+ final ValueLabelRenderer prevRenderer = getLabelRenderer();
+ super.setLabelRenderer(myRendererSettings.isBase(labelRenderer) ? null : labelRenderer);
+ final ValueLabelRenderer currentRenderer = getLabelRenderer();
+ if (prevRenderer != currentRenderer) {
+ if (currentRenderer instanceof ReferenceRenderer) {
+ ((ReferenceRenderer)currentRenderer).setClassName(getClassName());
+ }
+ }
+ }
+
+ public void setChildrenRenderer(ChildrenRenderer childrenRenderer) {
+ final ChildrenRenderer prevRenderer = getChildrenRenderer();
+ super.setChildrenRenderer(myRendererSettings.isBase(childrenRenderer) ? null : childrenRenderer);
+ final ChildrenRenderer currentRenderer = getChildrenRenderer();
+ if (prevRenderer != currentRenderer) {
+ if (currentRenderer instanceof ReferenceRenderer) {
+ ((ReferenceRenderer)currentRenderer).setClassName(getClassName());
+ }
+ }
+ }
+
+ public ChildrenRenderer getChildrenRenderer() {
+ final ChildrenRenderer childrenRenderer = super.getChildrenRenderer();
+ return childrenRenderer != null ? childrenRenderer : getDefaultRenderer();
+ }
+
+ private NodeRenderer getDefaultRenderer() {
+ return getClassName().endsWith("]") ? myRendererSettings.getArrayRenderer() : myRendererSettings.getClassRenderer();
+ }
+
+ public ValueLabelRenderer getLabelRenderer() {
+ final ValueLabelRenderer labelRenderer = super.getLabelRenderer();
+ return labelRenderer != null ? labelRenderer : getDefaultRenderer();
+ }
+
+ private ChildrenRenderer getRawChildrenRenderer() {
+ NodeRenderer classRenderer = getDefaultRenderer();
+ final ChildrenRenderer originalRenderer = super.getChildrenRenderer();
+ return originalRenderer == classRenderer ? null : originalRenderer;
+ }
+
+ private ValueLabelRenderer getRawLabelRenderer() {
+ NodeRenderer classRenderer = getDefaultRenderer();
+ final ValueLabelRenderer originalRenderer = super.getLabelRenderer();
+ return originalRenderer == classRenderer ? null : originalRenderer;
+ }
+
+
+ public boolean isApplicable(Type type) {
+ if(type == null || !(type instanceof ReferenceType) || !DebuggerUtils.instanceOf(type, getClassName())) {
+ return false;
+ }
+ return super.isApplicable(type);
+ }
+
+ public void setClassName(@NotNull String name) {
+ myProperties.setClassName(name);
+ if(getRawLabelRenderer() != null) {
+ final ValueLabelRenderer originalLabelRenderer = super.getLabelRenderer();
+ if (originalLabelRenderer instanceof ReferenceRenderer) {
+ ((ReferenceRenderer)originalLabelRenderer).setClassName(name);
+ }
+ }
+
+ if(getRawChildrenRenderer() != null) {
+ final ChildrenRenderer originalChildrenRenderer = super.getChildrenRenderer();
+ if (originalChildrenRenderer instanceof ReferenceRenderer) {
+ ((ReferenceRenderer)originalChildrenRenderer).setClassName(name);
+ }
+ }
+ }
+
+ public @NotNull String getClassName() {
+ return myProperties.getClassName();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/DescriptorLabelListener.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/DescriptorLabelListener.java
new file mode 100644
index 0000000..1158d99
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/DescriptorLabelListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+
+
+public interface DescriptorLabelListener {
+ DescriptorLabelListener DUMMY_LISTENER = new DescriptorLabelListener() {
+ public void labelChanged() {
+ }
+ };
+
+ void labelChanged();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/EnumerationChildrenRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/EnumerationChildrenRenderer.java
new file mode 100644
index 0000000..36878a2
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/EnumerationChildrenRenderer.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.descriptors.data.UserExpressionData;
+import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
+import com.intellij.debugger.ui.tree.*;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.Value;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Dec 19, 2003
+ * Time: 1:25:15 PM
+ */
+public final class EnumerationChildrenRenderer extends com.intellij.debugger.ui.tree.render.ReferenceRenderer implements ChildrenRenderer{
+ public static final @NonNls String UNIQUE_ID = "EnumerationChildrenRenderer";
+
+ private List<Pair<String, TextWithImports>> myChildren;
+ public static final @NonNls String CHILDREN_EXPRESSION = "ChildrenExpression";
+ public static final @NonNls String CHILD_NAME = "Name";
+
+ public EnumerationChildrenRenderer() {
+ this(new ArrayList<Pair<String, TextWithImports>>());
+ }
+
+ public EnumerationChildrenRenderer(List<Pair<String, TextWithImports>> children) {
+ super();
+ myChildren = children;
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public EnumerationChildrenRenderer clone() {
+ return (EnumerationChildrenRenderer)super.clone();
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+
+ myChildren.clear();
+
+ List<Element> children = element.getChildren(CHILDREN_EXPRESSION);
+ for (Iterator<Element> iterator = children.iterator(); iterator.hasNext();) {
+ Element item = iterator.next();
+
+ String name = item.getAttributeValue(CHILD_NAME);
+ TextWithImports text = DebuggerUtils.getInstance().readTextWithImports((Element) item.getChildren().get(0));
+
+ myChildren.add(new Pair<String, TextWithImports>(name, text));
+ }
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+
+ for (Iterator<Pair<String, TextWithImports>> iterator = myChildren.iterator(); iterator.hasNext();) {
+ Pair<String, TextWithImports> pair = iterator.next();
+ Element child = new Element(CHILDREN_EXPRESSION);
+ child.setAttribute(CHILD_NAME, pair.getFirst());
+ child.addContent(DebuggerUtils.getInstance().writeTextWithImports(pair.getSecond()));
+
+ element.addContent(child);
+ }
+ }
+
+ public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
+ NodeManager nodeManager = builder.getNodeManager();
+ NodeDescriptorFactory descriptorFactory = builder.getDescriptorManager();
+
+ List<DebuggerTreeNode> children = new ArrayList<DebuggerTreeNode>();
+ for (Pair<String, TextWithImports> pair : myChildren) {
+ children.add(nodeManager.createNode(descriptorFactory.getUserExpressionDescriptor(
+ builder.getParentDescriptor(),
+ new UserExpressionData((ValueDescriptorImpl)builder.getParentDescriptor(), getClassName(), pair.getFirst(), pair.getSecond())), evaluationContext)
+ );
+ }
+ builder.setChildren(children);
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
+ return ((ValueDescriptor) node.getDescriptor()).getDescriptorEvaluation(context);
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ return myChildren.size() > 0;
+ }
+
+ public List<Pair<String, TextWithImports>> getChildren() {
+ return myChildren;
+ }
+
+ public void setChildren(List<Pair<String, TextWithImports>> children) {
+ myChildren = children;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ExpressionChildrenRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ExpressionChildrenRenderer.java
new file mode 100644
index 0000000..b902b5b
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ExpressionChildrenRenderer.java
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.NodeManager;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.Value;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Sep 17, 2003
+ * Time: 2:04:00 PM
+ */
+public class ExpressionChildrenRenderer extends ReferenceRenderer implements ChildrenRenderer {
+ public static final @NonNls String UNIQUE_ID = "ExpressionChildrenRenderer";
+ private static final Key<Value> EXPRESSION_VALUE = new Key<Value>("EXPRESSION_VALUE");
+ private static final Key<NodeRenderer> LAST_CHILDREN_RENDERER = new Key<NodeRenderer>("LAST_CHILDREN_RENDERER");
+
+ private final CachedEvaluator myChildrenExpandable = new CachedEvaluator() {
+ protected String getClassName() {
+ return ExpressionChildrenRenderer.this.getClassName();
+ }
+ };
+
+ private final CachedEvaluator myChildrenExpression = new CachedEvaluator() {
+ protected String getClassName() {
+ return ExpressionChildrenRenderer.this.getClassName();
+ }
+ };
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public ExpressionChildrenRenderer clone() {
+ return (ExpressionChildrenRenderer)super.clone();
+ }
+
+ public void buildChildren(final Value value, final ChildrenBuilder builder, final EvaluationContext evaluationContext) {
+ final NodeManager nodeManager = builder.getNodeManager();
+
+ try {
+ final ValueDescriptor parentDescriptor = builder.getParentDescriptor();
+ final Value childrenValue = evaluateChildren(
+ evaluationContext.createEvaluationContext(value), parentDescriptor
+ );
+
+ NodeRenderer renderer = getLastChildrenRenderer(parentDescriptor);
+ if (renderer == null || childrenValue == null || !renderer.isApplicable(childrenValue.type())) {
+ renderer = DebugProcessImpl.getDefaultRenderer(childrenValue != null ? childrenValue.type() : null);
+ parentDescriptor.putUserData(LAST_CHILDREN_RENDERER, renderer);
+ }
+ renderer.buildChildren(childrenValue, builder, evaluationContext);
+ }
+ catch (final EvaluateException e) {
+ List<DebuggerTreeNode> errorChildren = new ArrayList<DebuggerTreeNode>();
+ errorChildren.add(nodeManager.createMessageNode(DebuggerBundle.message("error.unable.to.evaluate.expression") + " " + e.getMessage()));
+ builder.setChildren(errorChildren);
+ }
+ }
+
+ @Nullable
+ public static NodeRenderer getLastChildrenRenderer(ValueDescriptor descriptor) {
+ return descriptor.getUserData(LAST_CHILDREN_RENDERER);
+ }
+
+ public static void setPreferableChildrenRenderer(ValueDescriptor descriptor, NodeRenderer renderer) {
+ descriptor.putUserData(LAST_CHILDREN_RENDERER, renderer);
+ }
+
+ private Value evaluateChildren(EvaluationContext context, NodeDescriptor descriptor) throws EvaluateException {
+ final ExpressionEvaluator evaluator = myChildrenExpression.getEvaluator(context.getProject());
+
+ Value value = evaluator.evaluate(context);
+ descriptor.putUserData(EXPRESSION_VALUE, value);
+ return value;
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+ DefaultJDOMExternalizer.readExternal(this, element);
+
+ TextWithImports childrenExpression = DebuggerUtils.getInstance().readTextWithImports(element, "CHILDREN_EXPRESSION");
+ if(childrenExpression != null) {
+ setChildrenExpression(childrenExpression);
+ }
+
+ TextWithImports childrenExpandable = DebuggerUtils.getInstance().readTextWithImports(element, "CHILDREN_EXPANDABLE");
+ if(childrenExpandable != null) {
+ myChildrenExpandable.setReferenceExpression(childrenExpandable);
+ }
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ DebuggerUtils.getInstance().writeTextWithImports(element, "CHILDREN_EXPANDABLE", getChildrenExpandable());
+ DebuggerUtils.getInstance().writeTextWithImports(element, "CHILDREN_EXPRESSION", getChildrenExpression());
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
+ ValueDescriptor descriptor = (ValueDescriptor) node.getParent().getDescriptor();
+ Value expressionValue = descriptor.getUserData(EXPRESSION_VALUE);
+ if(expressionValue == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.unable.to.evaluate.expression"));
+ }
+
+ ChildrenRenderer defaultChildrenRenderer = ((DebugProcessImpl)context.getDebugProcess()).getDefaultRenderer(expressionValue.type());
+
+ return DebuggerUtils.getInstance().substituteThis(
+ defaultChildrenRenderer.getChildValueExpression(node, context),
+ (PsiExpression)myChildrenExpression.getPsiExpression(node.getProject()).copy(),
+ expressionValue, context);
+ }
+
+ public boolean isExpandable(Value value, final EvaluationContext context, NodeDescriptor parentDescriptor) {
+ final EvaluationContext evaluationContext = context.createEvaluationContext(value);
+
+ if(!"".equals(myChildrenExpandable.getReferenceExpression().getText())) {
+ try {
+ Value expanded = myChildrenExpandable.getEvaluator(evaluationContext.getProject()).evaluate(evaluationContext);
+ if(expanded instanceof BooleanValue) {
+ return ((BooleanValue)expanded).booleanValue();
+ }
+ }
+ catch (EvaluateException e) {
+ // ignored
+ }
+ }
+
+ try {
+ Value children = evaluateChildren(evaluationContext, parentDescriptor);
+
+ ChildrenRenderer defaultChildrenRenderer = ((DebugProcessImpl)evaluationContext.getDebugProcess()).getDefaultRenderer(value.type());
+
+ return defaultChildrenRenderer.isExpandable(children, evaluationContext, parentDescriptor);
+ }
+ catch (EvaluateException e) {
+ return true;
+ }
+ }
+
+ public TextWithImports getChildrenExpression() {
+ return myChildrenExpression.getReferenceExpression();
+ }
+
+ public void setChildrenExpression(TextWithImports expression) {
+ myChildrenExpression.setReferenceExpression(expression);
+ }
+
+ public TextWithImports getChildrenExpandable() {
+ return myChildrenExpandable.getReferenceExpression();
+ }
+
+ public void setChildrenExpandable(TextWithImports childrenExpandable) {
+ myChildrenExpandable.setReferenceExpression(childrenExpandable);
+ }
+
+ public void setClassName(String name) {
+ super.setClassName(name);
+ myChildrenExpression.clear();
+ myChildrenExpandable.clear();
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/HexRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/HexRenderer.java
new file mode 100644
index 0000000..6af36f0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/HexRenderer.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * User: lex
+ * Date: Nov 19, 2003
+ * Time: 3:13:11 PM
+ */
+public class HexRenderer extends NodeRendererImpl{
+ public static final @NonNls String UNIQUE_ID = "HexRenderer";
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.HexRenderer");
+
+ public HexRenderer() {
+ setEnabled(false);
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public @NonNls String getName() {
+ return "Hex";
+ }
+
+ public void setName(String name) {
+ // prohibit change
+ }
+
+ public HexRenderer clone() {
+ return (HexRenderer) super.clone();
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public String calcLabel(ValueDescriptor valueDescriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener) {
+ Value value = valueDescriptor.getValue();
+ StringBuilder buf = new StringBuilder(16);
+
+ if(value == null) {
+ buf.append("null");
+ }
+ else if (value instanceof CharValue) {
+ buf.append("'");
+ buf.append(value.toString());
+ buf.append("' ");
+ long longValue = ((PrimitiveValue)value).longValue();
+ buf.append("0x").append(Long.toHexString(longValue).toUpperCase());
+ }
+ else if (value instanceof ByteValue) {
+ byte val = ((PrimitiveValue)value).byteValue();
+ String strValue = Integer.toHexString(val).toUpperCase();
+ if (strValue.length() > 2) {
+ strValue = strValue.substring(strValue.length() - 2);
+ }
+ buf.append("0x").append(strValue);
+ }
+ else if (value instanceof ShortValue) {
+ short val = ((PrimitiveValue)value).shortValue();
+ String strValue = Integer.toHexString(val).toUpperCase();
+ if (strValue.length() > 4) {
+ strValue = strValue.substring(strValue.length() - 4);
+ }
+ buf.append("0x").append(strValue);
+ }
+ else if (value instanceof IntegerValue) {
+ int val = ((PrimitiveValue)value).intValue();
+ buf.append("0x").append(Integer.toHexString(val).toUpperCase());
+ }
+ else if (value instanceof LongValue) {
+ long val = ((PrimitiveValue)value).longValue();
+ buf.append("0x").append(Long.toHexString(val).toUpperCase());
+ }
+ else {
+ LOG.assertTrue(false);
+ }
+ return buf.toString();
+ }
+
+ public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
+ }
+
+ //returns whether this renderer is apllicable to this type or it's supertypes
+ public boolean isApplicable(Type t) {
+ if(t == null) {
+ return false;
+ }
+ return t instanceof CharType ||
+ t instanceof ByteType ||
+ t instanceof ShortType ||
+ t instanceof IntegerType ||
+ t instanceof LongType;
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) {
+ LOG.assertTrue(false);
+ return null;
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ return false;
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/LabelRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/LabelRenderer.java
new file mode 100644
index 0000000..31a457a
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/LabelRenderer.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.sun.jdi.PrimitiveValue;
+import com.sun.jdi.Value;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+
+/**
+ * User: lex
+ * Date: Sep 20, 2003
+ * Time: 10:27:12 PM
+ */
+public class LabelRenderer extends com.intellij.debugger.ui.tree.render.ReferenceRenderer implements ValueLabelRenderer{
+ public static final @NonNls String UNIQUE_ID = "LabelRenderer";
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.render.ClassLabelRenderer");
+
+ private final CachedEvaluator myLabelExpression = new CachedEvaluator() {
+ protected String getClassName() {
+ return LabelRenderer.this.getClassName();
+ }
+ };
+
+ public LabelRenderer() {
+ super();
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public LabelRenderer clone() {
+ return (LabelRenderer)super.clone();
+ }
+
+ public Icon calcValueIcon(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ return null;
+ }
+
+ public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener)
+ throws EvaluateException {
+
+ final Value value = descriptor.getValue();
+ LOG.assertTrue(!(value instanceof PrimitiveValue));
+
+ String result;
+ final DebugProcess debugProcess = evaluationContext.getDebugProcess();
+ if (value != null) {
+ try {
+ final ExpressionEvaluator evaluator = myLabelExpression.getEvaluator(debugProcess.getProject());
+
+ if(!debugProcess.isAttached()) {
+ throw EvaluateExceptionUtil.PROCESS_EXITED;
+ }
+ EvaluationContext thisEvaluationContext = evaluationContext.createEvaluationContext(value);
+ Value labelValue = evaluator.evaluate(thisEvaluationContext);
+ result = DebuggerUtils.convertToPresentationString(DebuggerUtils.getValueAsString(thisEvaluationContext, labelValue));
+ }
+ catch (final EvaluateException ex) {
+ throw new EvaluateException(DebuggerBundle.message("error.unable.to.evaluate.expression") + " " + ex.getMessage(), ex);
+ }
+ }
+ else {
+ //noinspection HardCodedStringLiteral
+ result = "null";
+ }
+ return result;
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+ DefaultJDOMExternalizer.readExternal(this, element);
+ TextWithImports labelExpression = DebuggerUtils.getInstance().readTextWithImports(element, "LABEL_EXPRESSION");
+ if (labelExpression != null) {
+ setLabelExpression(labelExpression);
+ }
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ DebuggerUtils.getInstance().writeTextWithImports(element, "LABEL_EXPRESSION", getLabelExpression());
+ }
+
+ public TextWithImports getLabelExpression() {
+ return myLabelExpression.getReferenceExpression();
+ }
+
+ public void setLabelExpression(TextWithImports expression) {
+ myLabelExpression.setReferenceExpression(expression);
+ }
+
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRenderer.java
new file mode 100644
index 0000000..aa7f8d9
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRenderer.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+public interface NodeRenderer extends ChildrenRenderer, ValueLabelRenderer {
+ String getName();
+
+ void setName(String text);
+
+ boolean isEnabled();
+
+ void setEnabled(boolean enabled);
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRendererImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRendererImpl.java
new file mode 100644
index 0000000..9062fa4
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRendererImpl.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import org.jdom.Element;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 9, 2005
+ */
+public abstract class NodeRendererImpl implements NodeRenderer{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.NodeRendererImpl");
+ protected BasicRendererProperties myProperties = new BasicRendererProperties();
+
+ protected NodeRendererImpl() {
+ //noinspection HardCodedStringLiteral
+ myProperties.setName("unnamed");
+ }
+
+ public String getName() {
+ return myProperties.getName();
+ }
+
+ public void setName(String name) {
+ myProperties.setName(name);
+ }
+
+ public boolean isEnabled() {
+ return myProperties.isEnabled();
+ }
+
+ public void setEnabled(boolean enabled) {
+ myProperties.setEnabled(enabled);
+ }
+
+ public Icon calcValueIcon(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException {
+ return null;
+ }
+
+ public NodeRendererImpl clone() {
+ try {
+ final NodeRendererImpl cloned = (NodeRendererImpl)super.clone();
+ cloned.myProperties = myProperties.clone();
+ return cloned;
+ }
+ catch (CloneNotSupportedException e) {
+ LOG.error(e);
+ }
+ return null;
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ myProperties.readExternal(element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ myProperties.writeExternal(element);
+ }
+
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRendererSettingsListener.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRendererSettingsListener.java
new file mode 100644
index 0000000..6108778
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/NodeRendererSettingsListener.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import java.util.EventListener;
+
+public interface NodeRendererSettingsListener extends EventListener{
+ void renderersChanged();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/PrimitiveRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/PrimitiveRenderer.java
new file mode 100644
index 0000000..d3e70b0
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/PrimitiveRenderer.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.PsiExpression;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * User: lex
+ * Date: Sep 18, 2003
+ * Time: 3:07:27 PM
+ */
+public class PrimitiveRenderer extends NodeRendererImpl {
+ public static final @NonNls String UNIQUE_ID = "PrimitiveRenderer";
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.PrimitiveRenderer");
+
+ public PrimitiveRenderer() {
+ //noinspection HardCodedStringLiteral
+ myProperties.setName("Primitive");
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public @NonNls String getName() {
+ return "Primitive";
+ }
+
+ public void setName(String text) {
+ // prohibit name change
+ }
+
+ public final boolean isEnabled() {
+ return true;
+ }
+
+ public void setEnabled(boolean enabled) {
+ // prohibit change
+ }
+
+ public boolean isApplicable(Type type) {
+ return type == null || type instanceof PrimitiveType || type instanceof VoidType;
+ }
+
+ public String calcLabel(ValueDescriptor valueDescriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener) {
+ Value value = valueDescriptor.getValue();
+ if(value == null) {
+ //noinspection HardCodedStringLiteral
+ return "null";
+ }
+ else if (value instanceof PrimitiveValue) {
+ StringBuilder buf = new StringBuilder(16);
+ if (value instanceof CharValue) {
+ buf.append("'");
+ buf.append(DebuggerUtils.translateStringValue(value.toString()));
+ buf.append("' ");
+ long longValue = ((PrimitiveValue)value).longValue();
+ buf.append(Long.toString(longValue));
+ }
+ else if (value instanceof ByteValue) {
+ buf.append(value.toString());
+ }
+ else if (value instanceof ShortValue) {
+ buf.append(value.toString());
+ }
+ else if (value instanceof IntegerValue) {
+ buf.append(value.toString());
+ }
+ else if (value instanceof LongValue) {
+ buf.append(value.toString());
+ }
+ else {
+ buf.append(value.toString());
+ }
+ return buf.toString();
+ }
+ else {
+ return DebuggerBundle.message("label.undefined");
+ }
+ }
+
+ public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
+ DebuggerManagerThreadImpl.assertIsManagerThread();
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) {
+ LOG.assertTrue(false);
+ return null;
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ return false;
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ReferenceRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ReferenceRenderer.java
new file mode 100644
index 0000000..9c664c3
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ReferenceRenderer.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.CommonClassNames;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.Type;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class ReferenceRenderer implements Renderer {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.ReferenceRenderer");
+ protected BasicRendererProperties myProperties = new BasicRendererProperties();
+
+ protected ReferenceRenderer() {
+ this(CommonClassNames.JAVA_LANG_OBJECT);
+ }
+
+ protected ReferenceRenderer(@NotNull String className) {
+ myProperties.setClassName(className);
+ }
+
+ public String getClassName() {
+ return myProperties.getClassName();
+ }
+
+ public void setClassName(String className) {
+ myProperties.setClassName(className);
+ }
+
+ public Renderer clone() {
+ try {
+ final ReferenceRenderer cloned = (ReferenceRenderer)super.clone();
+ cloned.myProperties = myProperties.clone();
+ return cloned;
+ }
+ catch (CloneNotSupportedException e) {
+ LOG.error(e);
+ }
+ return null;
+ }
+
+ public boolean isApplicable(Type type) {
+ return type != null && type instanceof ReferenceType && DebuggerUtils.instanceOf(type, getClassName());
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ myProperties.writeExternal(element);
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ myProperties.readExternal(element);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/Renderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/Renderer.java
new file mode 100644
index 0000000..3546f18
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/Renderer.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.sun.jdi.Type;
+
+public interface Renderer extends Cloneable, JDOMExternalizable {
+
+ String getUniqueId();
+
+ /***
+ * Checks whether this renderer is apllicable to this value
+ * @param type
+ */
+ boolean isApplicable(Type type);
+
+ Renderer clone();
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringCommand.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringCommand.java
new file mode 100644
index 0000000..0493bc5
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringCommand.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.SuspendContext;
+import com.intellij.debugger.engine.managerThread.SuspendContextCommand;
+import com.sun.jdi.Value;
+
+/**
+ * User: lex
+ * Date: Sep 16, 2003
+ * Time: 10:58:26 AM
+ */
+public abstract class ToStringCommand implements SuspendContextCommand {
+ private final EvaluationContext myEvaluationContext;
+ private final Value myValue;
+
+ private boolean myIsEvaluated = false;
+
+ protected ToStringCommand(EvaluationContext evaluationContext, Value value) {
+ myEvaluationContext = evaluationContext;
+ myValue = value;
+ }
+
+ public void action() {
+ if(myIsEvaluated) return;
+ try {
+ final String valueAsString = DebuggerUtils.getValueAsString(myEvaluationContext, myValue);
+ evaluationResult(valueAsString);
+ }
+ catch(final EvaluateException ex) {
+ evaluationError(ex.getMessage());
+ }
+ }
+
+ public void commandCancelled() {
+ }
+
+ public void setEvaluated() {
+ myIsEvaluated = true;
+ }
+
+ public SuspendContext getSuspendContext() {
+ return myEvaluationContext.getSuspendContext();
+ }
+
+ public abstract void evaluationResult(String message);
+ public abstract void evaluationError (String message);
+
+ public Value getValue() {
+ return myValue;
+ }
+
+ public EvaluationContext getEvaluationContext() {
+ return myEvaluationContext;
+ }
+}
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java
new file mode 100644
index 0000000..6a96753
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.DebuggerContext;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizerUtil;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.psi.CommonClassNames;
+import com.intellij.psi.PsiExpression;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
+import com.sun.jdi.*;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.Iterator;
+
+public class ToStringRenderer extends NodeRendererImpl {
+ public static final @NonNls String UNIQUE_ID = "ToStringRenderer";
+
+ private boolean USE_CLASS_FILTERS = false;
+ private ClassFilter[] myClassFilters = ClassFilter.EMPTY_ARRAY;
+
+ public ToStringRenderer() {
+ setEnabled(true);
+ }
+
+ public String getUniqueId() {
+ return UNIQUE_ID;
+ }
+
+ public @NonNls String getName() {
+ return "toString";
+ }
+
+ public void setName(String name) {
+ // prohibit change
+ }
+
+ public ToStringRenderer clone() {
+ final ToStringRenderer cloned = (ToStringRenderer)super.clone();
+ final ClassFilter[] classFilters = (myClassFilters.length > 0)? new ClassFilter[myClassFilters.length] : ClassFilter.EMPTY_ARRAY;
+ for (int idx = 0; idx < classFilters.length; idx++) {
+ classFilters[idx] = myClassFilters[idx].clone();
+ }
+ cloned.myClassFilters = classFilters;
+ return cloned;
+ }
+
+ public String calcLabel(final ValueDescriptor valueDescriptor, EvaluationContext evaluationContext, final DescriptorLabelListener labelListener)
+ throws EvaluateException {
+ final Value value = valueDescriptor.getValue();
+ BatchEvaluator.getBatchEvaluator(evaluationContext.getDebugProcess()).invoke(new ToStringCommand(evaluationContext, value) {
+ public void evaluationResult(String message) {
+ valueDescriptor.setValueLabel(
+ message == null? "" : "\"" + DebuggerUtils.convertToPresentationString(DebuggerUtilsEx.truncateString(message)) + "\""
+ );
+ labelListener.labelChanged();
+ }
+
+ public void evaluationError(String message) {
+ final String msg = value != null? message + " " + DebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", value.type().name()) : message;
+ valueDescriptor.setValueLabelFailed(new EvaluateException(msg, null));
+ labelListener.labelChanged();
+ }
+ });
+ return XDebuggerUIConstants.COLLECTING_DATA_MESSAGE;
+ }
+
+ public boolean isUseClassFilters() {
+ return USE_CLASS_FILTERS;
+ }
+
+ public void setUseClassFilters(boolean value) {
+ USE_CLASS_FILTERS = value;
+ }
+
+ public boolean isApplicable(Type type) {
+ if(!(type instanceof ReferenceType)) {
+ return false;
+ }
+
+ if(type.name().equals("java.lang.String")) {
+ return false; // do not render 'String' objects for performance reasons
+ }
+
+ if(!overridesToString(type)) {
+ return false;
+ }
+
+ if (USE_CLASS_FILTERS) {
+ if (!isFiltered(type)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static boolean overridesToString(Type type) {
+ if(type instanceof ClassType) {
+ final ClassType classType = (ClassType)type;
+ final java.util.List methods = classType.methodsByName("toString", "()Ljava/lang/String;");
+ if (methods.size() > 0) {
+ for (Iterator iterator = methods.iterator(); iterator.hasNext();) {
+ final Method method = (Method)iterator.next();
+ if(!(method.declaringType().name()).equals(CommonClassNames.JAVA_LANG_OBJECT)){
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
+ final DebugProcessImpl debugProcess = (DebugProcessImpl)evaluationContext.getDebugProcess();
+ debugProcess.getDefaultRenderer(value).buildChildren(value, builder, evaluationContext);
+ }
+
+ public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
+ final Value parentValue = ((ValueDescriptor)node.getParent().getDescriptor()).getValue();
+ final DebugProcessImpl debugProcess = (DebugProcessImpl)context.getDebugProcess();
+ return debugProcess.getDefaultRenderer(parentValue).getChildValueExpression(node, context);
+ }
+
+ public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
+ final DebugProcessImpl debugProcess = (DebugProcessImpl)evaluationContext.getDebugProcess();
+ return debugProcess.getDefaultRenderer(value).isExpandable(value, evaluationContext, parentDescriptor);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void readExternal(Element element) throws InvalidDataException {
+ super.readExternal(element);
+ final String value = JDOMExternalizerUtil.readField(element, "USE_CLASS_FILTERS");
+ USE_CLASS_FILTERS = "true".equalsIgnoreCase(value);
+ myClassFilters = DebuggerUtilsEx.readFilters(element.getChildren("filter"));
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(Element element) throws WriteExternalException {
+ super.writeExternal(element);
+ JDOMExternalizerUtil.writeField(element, "USE_CLASS_FILTERS", USE_CLASS_FILTERS? "true" : "false");
+ DebuggerUtilsEx.writeFilters(element, "filter", myClassFilters);
+ }
+
+ public ClassFilter[] getClassFilters() {
+ return myClassFilters;
+ }
+
+ public void setClassFilters(ClassFilter[] classFilters) {
+ myClassFilters = classFilters != null? classFilters : ClassFilter.EMPTY_ARRAY;
+ }
+
+ private boolean isFiltered(Type t) {
+ if (t instanceof ReferenceType) {
+ for (ClassFilter classFilter : myClassFilters) {
+ if (classFilter.isEnabled() && DebuggerUtilsEx.getSuperType(t, classFilter.getPattern()) != null) {
+ return true;
+ }
+ }
+ }
+ return DebuggerUtilsEx.isFiltered(t.name(), myClassFilters);
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ValueLabelRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ValueLabelRenderer.java
new file mode 100644
index 0000000..903f103
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ValueLabelRenderer.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * User: lex
+ * Date: Sep 20, 2003
+ * Time: 10:12:39 PM
+ */
+public interface ValueLabelRenderer extends Renderer {
+ String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException;
+
+ @Nullable
+ Icon calcValueIcon(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener) throws EvaluateException;
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassChildrenExpressionConfigurable.form b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassChildrenExpressionConfigurable.form
new file mode 100644
index 0000000..f592206
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassChildrenExpressionConfigurable.form
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.ui.tree.render.configurables.ClassChildrenExpressionConfigurable">
+ <grid id="1ddd" binding="myPanel" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="38" y="77" width="450" height="138"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="cb46f" class="com.intellij.openapi.ui.LabeledComponent" binding="myChildrenPanel">
+ <constraints>
+ <xy x="0" y="0" width="450" height="31"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1"/>
+ </constraints>
+ <properties>
+ <componentClass value="javax.swing.JPanel"/>
+ <labelInsets top="0" left="0" bottom="5" right="0"/>
+ <text resource-bundle="messages/DebuggerBundle" key="label.node.descendands.expression"/>
+ </properties>
+ </component>
+ <component id="5dbf7" class="com.intellij.openapi.ui.LabeledComponent" binding="myExpandablePanel">
+ <constraints>
+ <xy x="0" y="36" width="450" height="31"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1"/>
+ </constraints>
+ <properties>
+ <componentClass value="javax.swing.JPanel"/>
+ <labelInsets top="0" left="0" bottom="5" right="0"/>
+ <text resource-bundle="messages/DebuggerBundle" key="node.has.descendands.expression.optional"/>
+ </properties>
+ </component>
+ <vspacer id="2950c">
+ <constraints>
+ <xy x="219" y="67" width="11" height="71"/>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassChildrenExpressionConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassChildrenExpressionConfigurable.java
new file mode 100644
index 0000000..47faa68
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassChildrenExpressionConfigurable.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render.configurables;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.CompletionEditor;
+import com.intellij.debugger.ui.tree.render.ExpressionChildrenRenderer;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.LabeledComponent;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ClassChildrenExpressionConfigurable implements UnnamedConfigurable{
+ private final ExpressionChildrenRenderer myRenderer;
+
+ private JPanel myPanel;
+ private LabeledComponent<JPanel> myChildrenPanel;
+ private LabeledComponent<JPanel> myExpandablePanel;
+
+ private final CompletionEditor myChildrenEditor;
+ private final CompletionEditor myExpandableEditor;
+
+ public ClassChildrenExpressionConfigurable(Project project, ExpressionChildrenRenderer renderer) {
+ myRenderer = renderer;
+
+ PsiClass psiClass = DebuggerUtils.findClass(myRenderer.getClassName(), project, GlobalSearchScope.allScope(project));
+ myChildrenEditor = ((DebuggerUtilsEx)DebuggerUtils.getInstance()).createEditor(project, psiClass, "ClassChildrenExpression");
+ myExpandableEditor = ((DebuggerUtilsEx)DebuggerUtils.getInstance()).createEditor(project, psiClass, "ClassChildrenExpression");
+
+ myChildrenPanel.getComponent().setLayout(new BorderLayout());
+ myChildrenPanel.getComponent().add(myChildrenEditor);
+
+ myExpandablePanel.getComponent().setLayout(new BorderLayout());
+ myExpandablePanel.getComponent().add(myExpandableEditor);
+ }
+
+ public JComponent createComponent() {
+ return myPanel;
+ }
+
+ public boolean isModified() {
+ return !myRenderer.getChildrenExpression().equals(myChildrenEditor.getText()) ||
+ !myRenderer.getChildrenExpandable().equals(myExpandableEditor.getText());
+ }
+
+ public void apply() throws ConfigurationException {
+ myRenderer.setChildrenExpression(myChildrenEditor.getText());
+ myRenderer.setChildrenExpandable(myExpandableEditor.getText());
+ }
+
+ public void reset() {
+ myChildrenEditor.setText(myRenderer.getChildrenExpression());
+ myExpandableEditor.setText(myRenderer.getChildrenExpandable());
+ }
+
+ public void disposeUIResources() {
+ myChildrenEditor.dispose();
+ myExpandableEditor.dispose();
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassLabelExpressionConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassLabelExpressionConfigurable.java
new file mode 100644
index 0000000..ac8a7c6
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/ClassLabelExpressionConfigurable.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render.configurables;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.CompletionEditor;
+import com.intellij.debugger.ui.tree.render.LabelRenderer;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.LabeledComponent;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ClassLabelExpressionConfigurable implements UnnamedConfigurable{
+ private final LabelRenderer myRenderer;
+ private LabeledComponent<CompletionEditor> myCompletionEditor;
+ private final JPanel myPanel;
+
+ public ClassLabelExpressionConfigurable(@NotNull Project project, LabelRenderer renderer) {
+ myRenderer = renderer;
+
+ myCompletionEditor = new LabeledComponent<CompletionEditor>();
+ final PsiClass psiClass = DebuggerUtils.findClass(myRenderer.getClassName(), project, GlobalSearchScope.allScope(project));
+ myCompletionEditor.setComponent(((DebuggerUtilsEx)DebuggerUtils.getInstance()).createEditor(project, psiClass, "ClassLabelExpression"));
+ myCompletionEditor.setText(DebuggerBundle.message("label.class.label.expression.configurable.node.label"));
+
+ myPanel = new JPanel(new BorderLayout());
+ myPanel.add(myCompletionEditor, BorderLayout.NORTH);
+ }
+
+ public JComponent createComponent() {
+ return myPanel;
+ }
+
+ public boolean isModified() {
+ return !myRenderer.getLabelExpression().equals(myCompletionEditor.getComponent().getText());
+ }
+
+ public void apply() throws ConfigurationException {
+ myRenderer.setLabelExpression(myCompletionEditor.getComponent().getText());
+ }
+
+ public void reset() {
+ myCompletionEditor.getComponent().setText(myRenderer.getLabelExpression());
+ }
+
+ public void disposeUIResources() {
+ if (myCompletionEditor != null) {
+ myCompletionEditor.getComponent().dispose();
+ myCompletionEditor = null;
+ }
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/NamedChildrenConfigurable.form b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/NamedChildrenConfigurable.form
new file mode 100644
index 0000000..419e226
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/NamedChildrenConfigurable.form
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.ui.tree.render.configurables.NamedChildrenConfigurable">
+ <grid id="47245" binding="myPanel" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="57" y="23" width="298" height="252"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="a2d2a" class="javax.swing.JLabel" binding="myTableLabel">
+ <constraints>
+ <xy x="0" y="2" width="110" height="16"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="label.named.children.configurable.node.descendants"/>
+ </properties>
+ </component>
+ <grid id="c8237" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="0" y="25" width="298" height="227"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <scrollpane class="com.intellij.ui.components.JBScrollPane" id="68272">
+ <constraints>
+ <xy x="0" y="0" width="188" height="227"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="3"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="8776c" class="com.intellij.util.ui.Table" binding="myTable">
+ <constraints/>
+ <properties>
+ <preferredScrollableViewportSize width="-1" height="-1"/>
+ </properties>
+ </component>
+ </children>
+ </scrollpane>
+ <grid id="c7716" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="198" y="0" width="100" height="227"/>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="c1f20" class="javax.swing.JButton" binding="myButtonRemove">
+ <constraints>
+ <xy x="0" y="31" width="100" height="26"/>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="button.remove"/>
+ </properties>
+ </component>
+ <component id="f0acb" class="javax.swing.JButton" binding="myButtonUp">
+ <constraints>
+ <xy x="0" y="62" width="100" height="26"/>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="button.move.up"/>
+ </properties>
+ </component>
+ <component id="66bbb" class="javax.swing.JButton" binding="myButtonDown">
+ <constraints>
+ <xy x="0" y="93" width="100" height="26"/>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="button.move.down"/>
+ </properties>
+ </component>
+ <component id="5579c" class="javax.swing.JButton" binding="myButtonAdd">
+ <constraints>
+ <xy x="0" y="0" width="100" height="26"/>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/DebuggerBundle" key="button.add"/>
+ </properties>
+ </component>
+ <vspacer id="d11e5">
+ <constraints>
+ <xy x="44" y="119" width="11" height="108"/>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/NamedChildrenConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/NamedChildrenConfigurable.java
new file mode 100644
index 0000000..7e1515f
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/configurables/NamedChildrenConfigurable.java
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.ui.tree.render.configurables;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.debugger.impl.DebuggerUtilsEx;
+import com.intellij.debugger.ui.CompletionEditor;
+import com.intellij.debugger.ui.tree.render.EnumerationChildrenRenderer;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.TableUtil;
+import com.intellij.util.ui.AbstractTableCellEditor;
+import com.intellij.util.ui.Table;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.DefaultTableModel;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+
+public class NamedChildrenConfigurable implements UnnamedConfigurable{
+ private Table myTable;
+ private final EnumerationChildrenRenderer myRenderer;
+ private JPanel myPanel;
+ private JLabel myTableLabel;
+ private JButton myButtonAdd;
+ private JButton myButtonRemove;
+ private JButton myButtonUp;
+ private JButton myButtonDown;
+ private CompletionEditor myCompletionEditor;
+
+ public NamedChildrenConfigurable(Project project, EnumerationChildrenRenderer renderer) {
+ myRenderer = renderer;
+
+ myTableLabel.setLabelFor(myTable);
+
+ getModel().addColumn(DebuggerBundle.message("label.named.children.configurable.table.header.column.name"), (Object[])null);
+ final String expressionColumnName = DebuggerBundle.message("label.named.children.configurable.table.header.column.expression");
+ getModel().addColumn(expressionColumnName, (Object[])null);
+
+ PsiClass psiClass = DebuggerUtils.findClass(myRenderer.getClassName(), project, GlobalSearchScope.allScope(project));
+ myCompletionEditor = ((DebuggerUtilsEx)DebuggerUtils.getInstance()).createEditor(project, psiClass, "NamedChildrenConfigurable");
+
+ myTable.setDragEnabled(false);
+ myTable.setIntercellSpacing(new Dimension(0, 0));
+
+ myTable.getColumn(expressionColumnName).setCellEditor(new AbstractTableCellEditor() {
+ public Object getCellEditorValue() {
+ return myCompletionEditor.getText();
+ }
+
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+ myCompletionEditor.setText((TextWithImports)value);
+ return myCompletionEditor;
+ }
+ });
+
+ myButtonAdd.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ getModel().addRow(new Object[] {"", DebuggerUtils.getInstance().createExpressionWithImports("") });
+ }
+ });
+
+ myButtonRemove.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ int selectedRow = myTable.getSelectedRow();
+ if(selectedRow >= 0 && selectedRow < myTable.getRowCount()) {
+ getModel().removeRow(selectedRow);
+ }
+ }
+ });
+
+ myButtonDown.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ TableUtil.moveSelectedItemsDown(myTable);
+ }
+ });
+
+ myButtonUp.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ TableUtil.moveSelectedItemsUp(myTable);
+ }
+ });
+
+ myTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ updateButtons();
+ }
+ });
+ updateButtons();
+ }
+
+ private void updateButtons() {
+ int selectedRow = myTable.getSelectedRow();
+ myButtonRemove.setEnabled(selectedRow != -1);
+ myButtonUp.setEnabled(selectedRow > 0);
+ myButtonDown.setEnabled(selectedRow < myTable.getRowCount() - 1);
+ }
+
+ private DefaultTableModel getModel() {
+ return ((DefaultTableModel)myTable.getModel());
+ }
+
+ public JComponent createComponent() {
+ return myPanel;
+ }
+
+ public boolean isModified() {
+ return false;
+ }
+
+ public void apply() throws ConfigurationException {
+ DefaultTableModel model = getModel();
+
+ final int size = model.getRowCount();
+ java.util.List<Pair<String, TextWithImports>> result = new ArrayList<Pair<String, TextWithImports>>();
+
+ for (int idx = 0; idx < size; idx++) {
+ result.add(new Pair<String, TextWithImports>((String)model.getValueAt(idx, 0), (TextWithImports)model.getValueAt(idx, 1)));
+ }
+ myRenderer.setChildren(result);
+ }
+
+ public void reset() {
+ while(myTable.getModel().getRowCount() > 0) {
+ getModel().removeRow(0);
+ }
+
+ for (Pair<String, TextWithImports> pair : myRenderer.getChildren()) {
+ getModel().addRow(new Object[]{pair.getFirst(), pair.getSecond()});
+ }
+ }
+
+ public void disposeUIResources() {
+ if (myCompletionEditor != null) {
+ myCompletionEditor.dispose();
+ myCompletionEditor = null;
+ }
+ }
+
+
+ /*
+ private class TextWithImportsTableRenderer implements TableCellRenderer{
+ private final CompletionEditor myEditor;
+
+ private TextWithImportsTableRenderer () {
+ PsiClass psiClass = DebuggerUtils.findClass(myRenderer.getClassName(), myProject);
+ myEditor = DebuggerUtils.getInstance().createEditor(myProject, psiClass, "NamedChildrenConfigurable");
+ }
+
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ if(hasFocus) {
+ myEditor.setText((TextWithImports)value);
+ return myEditor;
+ }
+ else {
+ TableCellRenderer defaultRenderer = myTable.getDefaultRenderer(String.class);
+ return defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ }
+ }
+ }
+ */
+
+}