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);
+ }
+ }
+ }
+ */
+
+}
diff --git a/java/debugger/openapi/debugger-openapi.iml b/java/debugger/openapi/debugger-openapi.iml
new file mode 100644
index 0000000..52013f3
--- /dev/null
+++ b/java/debugger/openapi/debugger-openapi.iml
@@ -0,0 +1,168 @@
+<?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="execution-openapi" />
+ <orderEntry type="module" module-name="jsp-openapi" />
+ <orderEntry type="module" module-name="java-psi-api" />
+ <orderEntry type="module" module-name="resources-en" />
+ </component>
+ <component name="copyright">
+ <Base>
+ <setting name="state" value="0" />
+ </Base>
+ <LanguageOptions name="HTML">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="Copyright (c) &#36;today.year, Your Corporation. All Rights Reserved." />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="2" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ <LanguageOptions name="JAVA">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="/* * Copyright 2000-2005 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */" />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="2" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ <LanguageOptions name="JSP">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="Copyright (c) &#36;today.year, Your Corporation. All Rights Reserved." />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="2" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ <LanguageOptions name="JavaScript">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="Copyright (c) &#36;today.year, Your Corporation. All Rights Reserved." />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="2" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ <LanguageOptions name="Properties">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="Copyright (c) &#36;today.year, Your Corporation. All Rights Reserved." />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="2" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ <LanguageOptions name="XML">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="Copyright (c) &#36;today.year, Your Corporation. All Rights Reserved." />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="2" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ <LanguageOptions name="__TEMPLATE__">
+ <option name="templateOptions">
+ <value>
+ <option name="block" value="true" />
+ <option name="separateBefore" value="false" />
+ <option name="separateAfter" value="false" />
+ <option name="prefixLines" value="true" />
+ <option name="lenBefore" value="80" />
+ <option name="lenAfter" value="80" />
+ <option name="box" value="false" />
+ <option name="filler" value=" " />
+ </value>
+ </option>
+ <option name="notice" value="Copyright 2000-&#36;{today.year} JetBrains s.r.o. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." />
+ <option name="keyword" value="Copyright" />
+ <option name="fileTypeOverride" value="4" />
+ <option name="relativeBefore" value="true" />
+ <option name="addBlankAfter" value="true" />
+ <option name="fileLocation" value="1" />
+ <option name="useAlternate" value="false" />
+ </LanguageOptions>
+ </component>
+</module>
+
diff --git a/java/debugger/openapi/src/com/intellij/debugger/DebuggerBundle.java b/java/debugger/openapi/src/com/intellij/debugger/DebuggerBundle.java
new file mode 100644
index 0000000..179cf4a
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/DebuggerBundle.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;
+
+import com.intellij.CommonBundle;
+import com.intellij.execution.configurations.RemoteConnection;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.PropertyKey;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.ResourceBundle;
+
+public class DebuggerBundle {
+ private static Reference<ResourceBundle> ourBundle;
+
+ @NonNls private static final String BUNDLE = "messages.DebuggerBundle";
+
+ private DebuggerBundle() {
+ }
+
+ public static String getAddressDisplayName(final RemoteConnection connection) {
+ return connection.isUseSockets() ? connection.getHostName() + ":" + connection.getAddress() : connection.getAddress();
+ }
+
+ public static String getTransportName(final RemoteConnection connection) {
+ return connection.isUseSockets() ? message("transport.name.socket") : message("transport.name.shared.memory");
+ }
+
+ public static String message(@PropertyKey(resourceBundle = BUNDLE)String key, Object... params) {
+ return CommonBundle.message(getBundle(), key, params);
+ }
+
+ private static ResourceBundle getBundle() {
+ ResourceBundle bundle = null;
+ if (ourBundle != null) bundle = ourBundle.get();
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(BUNDLE);
+ ourBundle = new SoftReference<ResourceBundle>(bundle);
+ }
+ return bundle;
+ }
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.java b/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.java
new file mode 100644
index 0000000..d2c9180
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.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;
+
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.SuspendContext;
+import com.intellij.openapi.project.Project;
+
+public interface DebuggerContext extends StackFrameContext{
+ SuspendContext getSuspendContext();
+
+ Project getProject();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/DebuggerManager.java b/java/debugger/openapi/src/com/intellij/debugger/DebuggerManager.java
new file mode 100644
index 0000000..26b02c8
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/DebuggerManager.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;
+
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessListener;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.psi.PsiClass;
+import com.intellij.util.Function;
+
+/**
+ * @author lex
+ */
+public abstract class DebuggerManager implements ProjectComponent, JDOMExternalizable {
+ public static DebuggerManager getInstance(Project project) {
+ return project.getComponent(DebuggerManager.class);
+ }
+
+ public abstract DebugProcess getDebugProcess(ProcessHandler processHandler);
+
+ public abstract void addDebugProcessListener(ProcessHandler processHandler, DebugProcessListener listener);
+
+ public abstract void removeDebugProcessListener(ProcessHandler processHandler, DebugProcessListener listener);
+
+ public abstract boolean isDebuggerManagerThread();
+
+ public abstract void addClassNameMapper(NameMapper mapper);
+
+ public abstract void removeClassNameMapper(NameMapper mapper);
+
+ public abstract String getVMClassQualifiedName(PsiClass aClass);
+
+ /**
+ * @deprecated use PositionManagerFactory extension point instead
+ */
+ public abstract void registerPositionManagerFactory(Function<DebugProcess, PositionManager> factory);
+
+ public abstract void unregisterPositionManagerFactory(Function<DebugProcess, PositionManager> factory);
+
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/NameMapper.java b/java/debugger/openapi/src/com/intellij/debugger/NameMapper.java
new file mode 100644
index 0000000..4530806
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/NameMapper.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;
+
+import com.intellij.psi.PsiClass;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Compilers of some java-based languages (like Scala) produce classes with names different from those declared in sources.
+ * If for some language qualified names of compiled classes differ from names declared in the sources, a NameMapper must be registered
+ * within the DebuggerManager class.
+ * Multiple registered mappers forms a "chain of responsibility" and the first non-null result is returned.
+ * If no mappers are registered or all mappers returned null, the result of
+ * PsiClass.getQualifiedName() will be used as a qualified name of the compiled class
+ */
+public interface NameMapper {
+ /**
+ * @param aClass a top-level class
+ * @return a qualified name of the corresponding compiled class or null if default machanism of getting qualified names must be used
+ */
+ String getQualifiedName(@NotNull PsiClass aClass);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/NoDataException.java b/java/debugger/openapi/src/com/intellij/debugger/NoDataException.java
new file mode 100644
index 0000000..b3f30af
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/NoDataException.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;
+
+public class NoDataException extends Exception{
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/PositionManager.java b/java/debugger/openapi/src/com/intellij/debugger/PositionManager.java
new file mode 100644
index 0000000..5713483
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/PositionManager.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;
+
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+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.List;
+
+/**
+ * Manages the correspondence between source positions and bytecode locations during JVM debugging.
+ * Instances of this class are created by the factory registered via the {@link PositionManagerFactory} extension point.
+ *
+ * @see com.intellij.debugger.engine.JSR45PositionManager
+ */
+public interface PositionManager {
+ /**
+ * Returns the source position corresponding to the specified bytecode location.
+ *
+ * @param location the bytecode location.
+ * @return the corresponding source position.
+ * @throws NoDataException if the location is not in the code managed by this {@code PositionManager}
+ */
+ @Nullable
+ SourcePosition getSourcePosition(@Nullable Location location) throws NoDataException;
+
+ /**
+ * Returns the list of all Java classes corresponding to the specified position in the source code.
+ *
+ * @param classPosition the source position.
+ * @return the list of corresponding Java classes.
+ * @throws NoDataException if the location is not in the code managed by this {@code PositionManager}
+ * @see com.intellij.debugger.engine.jdi.VirtualMachineProxy#classesByName
+ */
+ @NotNull
+ List<ReferenceType> getAllClasses(SourcePosition classPosition) throws NoDataException;
+
+ /**
+ * Returns the list of bytecode locations in a specific class corresponding to the specified position in the source code.
+ *
+ * @param type a Java class (one of the list returned by {@link #getAllClasses}).
+ * @param position the position in the source code.
+ * @return the list of corresponding bytecode locations.
+ * @throws NoDataException if the location is not in the code managed by this {@code PositionManager}
+ * @see ReferenceType#locationsOfLine(int)
+ */
+ @NotNull
+ List<Location> locationsOfLine(ReferenceType type, SourcePosition position) throws NoDataException;
+
+ /**
+ * Called to request the JVM to notify the debugger engine when a class corresponding to a breakpoint location is loaded.
+ * The implementation should calculate the pattern of the class files corresponding to the breakpoint location and call
+ * {@link com.intellij.debugger.requests.RequestManager#createClassPrepareRequest} to create the request.
+ *
+ * @param requestor the object to receive the notification from the JVM.
+ * @param position the location of a breakpoint.
+ * @return the prepare request, or null if the code is managed by this {@code PositionManager} but no class prepare notification is needed
+ * @throws NoDataException if the position is not in the code managed by this {@code PositionManager}
+ */
+ @Nullable
+ ClassPrepareRequest createPrepareRequest(ClassPrepareRequestor requestor, SourcePosition position) throws NoDataException;
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java b/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java
new file mode 100644
index 0000000..e89d423
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author yole
+ */
+public abstract class PositionManagerFactory {
+ public static final ExtensionPointName<PositionManagerFactory> EP_NAME = ExtensionPointName.create("com.intellij.debugger.positionManagerFactory");
+
+ @Nullable
+ public abstract PositionManager createPositionManager(DebugProcess process);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java b/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java
new file mode 100644
index 0000000..9028b14
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import com.intellij.psi.*;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * User: lex
+ * Date: Oct 24, 2003
+ * Time: 8:23:06 PM
+ */
+public abstract class SourcePosition implements Navigatable{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.SourcePosition");
+ @NotNull
+ public abstract PsiFile getFile();
+
+ public abstract PsiElement getElementAt();
+
+ /**
+ * @return a zero-based line number
+ */
+ public abstract int getLine();
+
+ public abstract int getOffset();
+
+ public abstract Editor openEditor(boolean requestFocus);
+
+ private abstract static class SourcePositionCache extends SourcePosition {
+ @NotNull private final PsiFile myFile;
+ private long myModificationStamp = -1L;
+
+ private PsiElement myPsiElement;
+ private Integer myLine;
+ private Integer myOffset;
+
+ public SourcePositionCache(@NotNull PsiFile file) {
+ myFile = file;
+ updateData();
+ }
+
+ @Override
+ @NotNull
+ public PsiFile getFile() {
+ return myFile;
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return getFile().isValid();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return canNavigate();
+ }
+
+ @Override
+ public void navigate(final boolean requestFocus) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (!canNavigate()) {
+ return;
+ }
+ openEditor(requestFocus);
+ }
+ });
+ }
+
+ @Override
+ public Editor openEditor(final boolean requestFocus) {
+ final PsiFile psiFile = getFile();
+ final Project project = psiFile.getProject();
+ if (project.isDisposed()) {
+ return null;
+ }
+ final VirtualFile virtualFile = psiFile.getVirtualFile();
+ if (virtualFile == null || !virtualFile.isValid()) {
+ return null;
+ }
+ final int offset = getOffset();
+ if (offset < 0) {
+ return null;
+ }
+ return FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, virtualFile, offset), requestFocus);
+ }
+
+ private void updateData() {
+ if(dataUpdateNeeded()) {
+ myModificationStamp = myFile.getModificationStamp();
+ myLine = null;
+ myOffset = null;
+ myPsiElement = null;
+ }
+ }
+
+ private boolean dataUpdateNeeded() {
+ if (myModificationStamp != myFile.getModificationStamp()) {
+ return true;
+ }
+ final PsiElement psiElement = myPsiElement;
+ return psiElement != null && !psiElement.isValid();
+ }
+
+ @Override
+ public int getLine() {
+ updateData();
+ if (myLine == null) {
+ myLine = calcLine();
+ }
+ return myLine.intValue();
+ }
+
+ @Override
+ public int getOffset() {
+ updateData();
+ if (myOffset == null) {
+ myOffset = calcOffset();
+ }
+ return myOffset.intValue();
+ }
+
+ @Override
+ public PsiElement getElementAt() {
+ updateData();
+ if (myPsiElement == null) {
+ myPsiElement = calcPsiElement();
+ }
+ return myPsiElement;
+ }
+
+ protected int calcLine() {
+ final PsiFile file = getFile();
+ Document document = null;
+ try {
+ document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
+ }
+ catch (Throwable e) {
+ LOG.error(e);
+ }
+ if (document != null) {
+ try {
+ return document.getLineNumber(calcOffset());
+ }
+ catch (IndexOutOfBoundsException e) {
+ // may happen if document has been changed since the this SourcePosition was created
+ }
+ }
+ return -1;
+ }
+
+ protected int calcOffset() {
+ final PsiFile file = getFile();
+ final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
+ if (document != null) {
+ try {
+ return document.getLineStartOffset(calcLine());
+ }
+ catch (IndexOutOfBoundsException e) {
+ // may happen if document has been changed since the this SourcePosition was created
+ }
+ }
+ return -1;
+ }
+
+ @Nullable
+ protected PsiElement calcPsiElement() {
+ PsiFile psiFile = getFile();
+ int lineNumber = getLine();
+ if(lineNumber < 0) {
+ return psiFile;
+ }
+
+ final Document document = PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile);
+ if (document == null) {
+ return null;
+ }
+ if (lineNumber >= document.getLineCount()) {
+ return psiFile;
+ }
+ int startOffset = document.getLineStartOffset(lineNumber);
+ if(startOffset == -1) {
+ return null;
+ }
+
+ PsiElement rootElement = psiFile;
+
+ List<PsiFile> allFiles = psiFile.getViewProvider().getAllFiles();
+ if (allFiles.size() > 1) { // jsp & gsp
+ PsiClassOwner owner = ContainerUtil.findInstance(allFiles, PsiClassOwner.class);
+ if (owner != null) {
+ PsiClass[] classes = owner.getClasses();
+ if (classes.length == 1 && classes[0] instanceof SyntheticElement) {
+ rootElement = classes[0];
+ }
+ }
+ }
+
+ PsiElement element;
+ while(true) {
+ final CharSequence charsSequence = document.getCharsSequence();
+ for (; startOffset < charsSequence.length(); startOffset++) {
+ char c = charsSequence.charAt(startOffset);
+ if (c != ' ' && c != '\t') {
+ break;
+ }
+ }
+ element = rootElement.findElementAt(startOffset);
+
+ if(element instanceof PsiComment) {
+ startOffset = element.getTextRange().getEndOffset() + 1;
+ }
+ else{
+ break;
+ }
+ }
+
+ if (element != null && element.getParent() instanceof PsiForStatement) {
+ return ((PsiForStatement)element.getParent()).getInitialization();
+ }
+ return element;
+ }
+ }
+
+ public static SourcePosition createFromLineComputable(final PsiFile file, final Computable<Integer> line) {
+ return new SourcePositionCache(file) {
+ @Override
+ protected int calcLine() {
+ return line.compute();
+ }
+ };
+ }
+
+ public static SourcePosition createFromLine(final PsiFile file, final int line) {
+ return new SourcePositionCache(file) {
+ @Override
+ protected int calcLine() {
+ return line;
+ }
+ };
+ }
+
+ public static SourcePosition createFromOffset(final PsiFile file, final int offset) {
+ return new SourcePositionCache(file) {
+
+ @Override
+ protected int calcOffset() {
+ return offset;
+ }
+ };
+ }
+
+ public static SourcePosition createFromElement(PsiElement element) {
+ final PsiElement navigationElement = element.getNavigationElement();
+ final PsiFile psiFile;
+ if (JspPsiUtil.isInJspFile(navigationElement)) {
+ psiFile = JspPsiUtil.getJspFile(navigationElement);
+ }
+ else {
+ psiFile = navigationElement.getContainingFile();
+ }
+ return new SourcePositionCache(psiFile) {
+ @Override
+ protected PsiElement calcPsiElement() {
+ return navigationElement;
+ }
+
+ @Override
+ protected int calcOffset() {
+ return navigationElement.getTextOffset();
+ }
+ };
+ }
+
+ public boolean equals(Object o) {
+ if(o instanceof SourcePosition) {
+ SourcePosition sourcePosition = (SourcePosition)o;
+ return Comparing.equal(sourcePosition.getFile(), getFile()) && sourcePosition.getOffset() == getOffset();
+ }
+
+ return false;
+ }
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java
new file mode 100644
index 0000000..bca8159
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.PositionManager;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.jdi.VirtualMachineProxy;
+import com.intellij.debugger.engine.managerThread.DebuggerManagerThread;
+import com.intellij.debugger.requests.RequestManager;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author lex
+ */
+public interface DebugProcess {
+ @NonNls String JAVA_STRATUM = "Java";
+
+ <T> T getUserData(Key<T> key);
+ <T> void putUserData(Key<T> key, T value);
+
+ Project getProject();
+
+ RequestManager getRequestsManager();
+
+ PositionManager getPositionManager();
+
+ VirtualMachineProxy getVirtualMachineProxy();
+
+ void addDebugProcessListener(DebugProcessListener listener);
+
+ void removeDebugProcessListener(DebugProcessListener listener);
+
+ void appendPositionManager(PositionManager positionManager);
+
+ void waitFor();
+
+ void waitFor(long timeout);
+
+ void stop(boolean forceTerminate);
+
+ ExecutionResult getExecutionResult();
+
+ DebuggerManagerThread getManagerThread();
+
+ Value invokeMethod(EvaluationContext evaluationContext,
+ ObjectReference objRef,
+ Method method,
+ List args) throws EvaluateException;
+
+ /**
+ * Is equivalent to invokeInstanceMethod(evaluationContext, classType, method, args, 0)
+ */
+ Value invokeMethod(EvaluationContext evaluationContext,
+ ClassType classType,
+ Method method,
+ List args) throws EvaluateException;
+
+ Value invokeInstanceMethod(EvaluationContext evaluationContext,
+ ObjectReference objRef,
+ Method method,
+ List args,
+ int invocationOptions) throws EvaluateException;
+
+ ReferenceType findClass(EvaluationContext evaluationContext,
+ String name,
+ ClassLoaderReference classLoader) throws EvaluateException;
+
+ ArrayReference newInstance(ArrayType arrayType,
+ int dimension) throws EvaluateException;
+
+ ObjectReference newInstance(EvaluationContext evaluationContext,
+ ClassType classType,
+ Method constructor,
+ List paramList) throws EvaluateException;
+
+ boolean isAttached();
+
+ boolean isDetached();
+
+ boolean isDetaching();
+
+ /**
+ * @return the search scope used by debugger to find sources corresponding to classes being executed
+ */
+ @NotNull
+ GlobalSearchScope getSearchScope();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java
new file mode 100644
index 0000000..16c43c2
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+public class DebugProcessAdapter implements DebugProcessListener{
+ //executed in manager thread
+ public void connectorIsReady() {
+ }
+
+ //executed in manager thread
+ public void paused(SuspendContext suspendContext) {
+
+ }
+
+ //executed in manager thread
+ public void resumed(SuspendContext suspendContext) {
+
+ }
+
+ //executed in manager thread
+ public void processDetached(DebugProcess process, boolean closedByUser) {
+
+ }
+
+ //executed in manager thread
+ public void processAttached(DebugProcess process) {
+
+ }
+
+ //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/openapi/src/com/intellij/debugger/engine/DebugProcessListener.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessListener.java
new file mode 100644
index 0000000..e992f25
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessListener.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.engine;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.sun.jdi.ThreadReference;
+
+import java.util.EventListener;
+
+
+public interface DebugProcessListener extends EventListener{
+ //executed in manager thread
+ void connectorIsReady();
+
+ //executed in manager thread
+ void paused(SuspendContext suspendContext);
+
+ //executed in manager thread
+ void resumed(SuspendContext suspendContext);
+
+ //executed in manager thread
+ void processDetached(DebugProcess process, boolean closedByUser);
+
+ //executed in manager thread
+ void processAttached(DebugProcess process);
+
+ void attachException(RunProfileState state, ExecutionException exception, RemoteConnection remoteConnection);
+
+ void threadStarted(DebugProcess proc, ThreadReference thread);
+
+ void threadStopped(DebugProcess proc, ThreadReference thread);
+}
+
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java
new file mode 100644
index 0000000..418e442
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DebuggerContext;
+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.execution.ExecutionException;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.InheritanceUtil;
+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.Nullable;
+
+import java.util.*;
+
+public abstract class DebuggerUtils {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebuggerUtils");
+ private static final Key<Method> TO_STRING_METHOD_KEY = new Key<Method>("CachedToStringMethod");
+ public static final Set<String> ourPrimitiveTypeNames = new HashSet<String>(Arrays.asList(
+ "byte", "short", "int", "long", "float", "double", "boolean", "char"
+ ));
+
+ public static void cleanupAfterProcessFinish(DebugProcess debugProcess) {
+ debugProcess.putUserData(TO_STRING_METHOD_KEY, null);
+ }
+
+ @NonNls
+ public static String getValueAsString(final EvaluationContext evaluationContext, Value value) throws EvaluateException {
+ try {
+ if (value == null) {
+ return "null";
+ }
+ if (value instanceof StringReference) {
+ return ((StringReference)value).value();
+ }
+ if (isInteger(value)) {
+ long v = ((PrimitiveValue)value).longValue();
+ return String.valueOf(v);
+ }
+ if (isNumeric(value)) {
+ double v = ((PrimitiveValue)value).doubleValue();
+ return String.valueOf(v);
+ }
+ if (value instanceof BooleanValue) {
+ boolean v = ((PrimitiveValue)value).booleanValue();
+ return String.valueOf(v);
+ }
+ if (value instanceof CharValue) {
+ char v = ((PrimitiveValue)value).charValue();
+ return String.valueOf(v);
+ }
+ if (value instanceof ObjectReference) {
+ if (value instanceof ArrayReference) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ for (Iterator<Value> iterator = ((ArrayReference)value).getValues().iterator(); iterator.hasNext();) {
+ final Value element = iterator.next();
+ builder.append(getValueAsString(evaluationContext, element));
+ if (iterator.hasNext()) {
+ builder.append(",");
+ }
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ final ObjectReference objRef = (ObjectReference)value;
+ final DebugProcess debugProcess = evaluationContext.getDebugProcess();
+ Method toStringMethod = debugProcess.getUserData(TO_STRING_METHOD_KEY);
+ if (toStringMethod == null) {
+ try {
+ ReferenceType refType = objRef.virtualMachine().classesByName(CommonClassNames.JAVA_LANG_OBJECT).get(0);
+ toStringMethod = findMethod(refType, "toString", "()Ljava/lang/String;");
+ debugProcess.putUserData(TO_STRING_METHOD_KEY, toStringMethod);
+ }
+ catch (Exception e) {
+ throw EvaluateExceptionUtil.createEvaluateException(
+ DebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", objRef.referenceType().name()));
+ }
+ }
+ if (toStringMethod == null) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", objRef.referenceType().name()));
+ }
+ final StringReference stringReference = (StringReference)debugProcess.invokeInstanceMethod(evaluationContext, objRef, toStringMethod, Collections.emptyList(), 0);
+ return stringReference == null ? "null" : stringReference.value();
+ }
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.unsupported.expression.type"));
+ }
+ catch (ObjectCollectedException e) {
+ throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
+ }
+ }
+
+ public static final int MAX_DISPLAY_LABEL_LENGTH = 1024/*kb*/ *1024 /*bytes*/ / 2; // 1 Mb string
+
+ public static String convertToPresentationString(String str) {
+ if (str.length() > MAX_DISPLAY_LABEL_LENGTH) {
+ str = translateStringValue(str.substring(0, MAX_DISPLAY_LABEL_LENGTH));
+ StringBuilder buf = new StringBuilder();
+ buf.append(str);
+ if (!str.endsWith("...")) {
+ buf.append("...");
+ }
+ return buf.toString();
+ }
+ return translateStringValue(str);
+ }
+
+ @Nullable
+ public static Method findMethod(ReferenceType refType, @NonNls String methodName, @NonNls String methodSignature) {
+ if (refType instanceof ArrayType) {
+ // for array types methodByName() in JDI always returns empty list
+ final Method method = findMethod(refType.virtualMachine().classesByName(CommonClassNames.JAVA_LANG_OBJECT).get(0), methodName, methodSignature);
+ if (method != null) {
+ return method;
+ }
+ }
+
+ Method method = null;
+ if (methodSignature != null) {
+ if (refType instanceof ClassType) {
+ method = ((ClassType)refType).concreteMethodByName(methodName, methodSignature);
+ }
+ if (method == null) {
+ final List<Method> methods = refType.methodsByName(methodName, methodSignature);
+ if (methods.size() > 0) {
+ method = methods.get(0);
+ }
+ }
+ }
+ else {
+ List<Method> methods = null;
+ if (refType instanceof ClassType) {
+ methods = refType.methodsByName(methodName);
+ }
+ if (methods != null && methods.size() > 0) {
+ method = methods.get(0);
+ }
+ }
+ return method;
+ }
+
+ public static boolean isNumeric(Value value) {
+ return value != null &&
+ (isInteger(value) ||
+ value instanceof FloatValue ||
+ value instanceof DoubleValue
+ );
+ }
+
+ public static boolean isInteger(Value value) {
+ return value != null &&
+ (value instanceof ByteValue ||
+ value instanceof ShortValue ||
+ value instanceof LongValue ||
+ value instanceof IntegerValue
+ );
+ }
+
+ public static String translateStringValue(final String str) {
+ int length = str.length();
+ final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ StringUtil.escapeStringCharacters(length, str, buffer);
+ if (str.length() > length) {
+ buffer.append("...");
+ }
+ return buffer.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ protected static ArrayClass getArrayClass(String className) {
+ boolean searchBracket = false;
+ int dims = 0;
+ int pos;
+
+ for(pos = className.lastIndexOf(']'); pos >= 0; pos--){
+ char c = className.charAt(pos);
+
+ if (searchBracket) {
+ if (c == '[') {
+ dims++;
+ searchBracket = false;
+ }
+ else if (!Character.isWhitespace(c)) break;
+ }
+ else {
+ if (c == ']') {
+ searchBracket = true;
+ }
+ else if (!Character.isWhitespace(c)) break;
+ }
+ }
+
+ if (searchBracket) return null;
+
+ if(dims == 0) return null;
+
+ return new ArrayClass(className.substring(0, pos + 1), dims);
+ }
+
+ public static boolean instanceOf(String subType, String superType, Project project) {
+ if(project == null) {
+ return subType.equals(superType);
+ }
+
+ ArrayClass nodeClass = getArrayClass(subType);
+ ArrayClass rendererClass = getArrayClass(superType);
+ if (nodeClass == null || rendererClass == null) return false;
+
+ if (nodeClass.dims == rendererClass.dims) {
+ GlobalSearchScope scope = GlobalSearchScope.allScope(project);
+ PsiClass psiNodeClass = JavaPsiFacade.getInstance(project).findClass(nodeClass.className, scope);
+ PsiClass psiRendererClass = JavaPsiFacade.getInstance(project).findClass(rendererClass.className, scope);
+ return InheritanceUtil.isInheritorOrSelf(psiNodeClass, psiRendererClass, true);
+ }
+ else if (nodeClass.dims > rendererClass.dims) {
+ return rendererClass.className.equals(CommonClassNames.JAVA_LANG_OBJECT);
+ }
+ return false;
+ }
+
+ public static Type getSuperType(Type subType, String superType) {
+ if(CommonClassNames.JAVA_LANG_OBJECT.equals(superType)) {
+ List list = subType.virtualMachine().classesByName(CommonClassNames.JAVA_LANG_OBJECT);
+ if(list.size() > 0) {
+ return (ReferenceType)list.get(0);
+ }
+ return null;
+ }
+
+ return getSuperTypeInt(subType, superType);
+ }
+
+ private static Type getSuperTypeInt(Type subType, String superType) {
+ Type result;
+ if (subType == null) {
+ return null;
+ }
+
+ if (subType.name().equals(superType)) {
+ return subType;
+ }
+
+ if (subType instanceof ClassType) {
+ result = getSuperType(((ClassType)subType).superclass(), superType);
+ if (result != null) {
+ return result;
+ }
+
+ List ifaces = ((ClassType)subType).allInterfaces();
+ for (Iterator iterator = ifaces.iterator(); iterator.hasNext();) {
+ InterfaceType interfaceType = (InterfaceType)iterator.next();
+ if (interfaceType.name().equals(superType)) {
+ return interfaceType;
+ }
+ }
+ return null;
+ }
+
+ if (subType instanceof InterfaceType) {
+ List ifaces = ((InterfaceType)subType).superinterfaces();
+ for (Iterator iterator = ifaces.iterator(); iterator.hasNext();) {
+ InterfaceType interfaceType = (InterfaceType)iterator.next();
+ result = getSuperType(interfaceType, superType);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ else if (subType instanceof ArrayType) {
+ if (superType.endsWith("[]")) {
+ try {
+ String superTypeItem = superType.substring(0, superType.length() - 2);
+ Type subTypeItem = ((ArrayType)subType).componentType();
+ return instanceOf(subTypeItem, superTypeItem) ? subType : null;
+ }
+ catch (ClassNotLoadedException e) {
+ LOG.debug(e);
+ }
+ }
+ }
+ else if (subType instanceof PrimitiveType) {
+ //noinspection HardCodedStringLiteral
+ if(superType.equals("java.lang.Primitive")) {
+ return subType;
+ }
+ }
+
+ //only for interfaces and arrays
+ if(CommonClassNames.JAVA_LANG_OBJECT.equals(superType)) {
+ List list = subType.virtualMachine().classesByName(CommonClassNames.JAVA_LANG_OBJECT);
+ if(list.size() > 0) {
+ return (ReferenceType)list.get(0);
+ }
+ }
+ return null;
+ }
+
+ public static boolean instanceOf(Type subType, String superType) {
+ return getSuperType(subType, superType) != null;
+ }
+
+ @Nullable
+ public static PsiClass findClass(final String className, Project project, final GlobalSearchScope scope) {
+ ApplicationManager.getApplication().assertReadAccessAllowed();
+ final PsiManager psiManager = PsiManager.getInstance(project);
+ final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(psiManager.getProject());
+ if (getArrayClass(className) != null) {
+ return javaPsiFacade.getElementFactory().getArrayClass(LanguageLevelProjectExtension.getInstance(psiManager.getProject()).getLanguageLevel());
+ }
+ if(project.isDefault()) {
+ return null;
+ }
+ final String _className = className.replace('$', '.');
+ PsiClass aClass = javaPsiFacade.findClass(_className, scope);
+ if (aClass == null) {
+ if (!_className.equals(className)) {
+ // try original name if it differs from the normalized name
+ aClass = javaPsiFacade.findClass(className, scope);
+ }
+ }
+ if (aClass == null) {
+ final GlobalSearchScope globalScope = GlobalSearchScope.allScope(project);
+ if (!globalScope.equals(scope)) {
+ aClass = javaPsiFacade.findClass(_className, globalScope);
+ if (aClass == null) {
+ if (!_className.equals(className)) {
+ // try original name with global scope if the original differs from the normalized name
+ aClass = javaPsiFacade.findClass(className, globalScope);
+ }
+ }
+ }
+ }
+ return aClass;
+ }
+
+ public static PsiType getType(String className, Project project) {
+ ApplicationManager.getApplication().assertReadAccessAllowed();
+
+ final PsiManager psiManager = PsiManager.getInstance(project);
+ try {
+ if (getArrayClass(className) != null) {
+ return JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory().createTypeFromText(className, null);
+ }
+ if(project.isDefault()) {
+ return null;
+ }
+ final PsiClass aClass =
+ JavaPsiFacade.getInstance(psiManager.getProject()).findClass(className.replace('$', '.'), GlobalSearchScope.allScope(project));
+ return JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory().createType(aClass);
+ }
+ catch (IncorrectOperationException e) {
+ LOG.error(e);
+ }
+ return null;
+ }
+
+ public static void checkSyntax(PsiCodeFragment codeFragment) throws EvaluateException {
+ PsiElement[] children = codeFragment.getChildren();
+
+ if(children.length == 0) throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.empty.code.fragment"));
+ for (int i = 0; i < children.length; i++) {
+ PsiElement child = children[i];
+ if(child instanceof PsiErrorElement) {
+ throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", child.getText()));
+ }
+ }
+ }
+
+ public static boolean hasSideEffects(PsiElement element) {
+ return hasSideEffectsOrReferencesMissingVars(element, null);
+ }
+
+ public static boolean hasSideEffectsOrReferencesMissingVars(PsiElement element, @Nullable final Set<String> visibleLocalVariables) {
+ final Ref<Boolean> rv = new Ref<Boolean>(Boolean.FALSE);
+ element.accept(new JavaRecursiveElementWalkingVisitor() {
+ @Override
+ public void visitPostfixExpression(final PsiPostfixExpression expression) {
+ rv.set(Boolean.TRUE);
+ }
+
+ @Override
+ public void visitReferenceExpression(final PsiReferenceExpression expression) {
+ final PsiElement psiElement = expression.resolve();
+ if (psiElement instanceof PsiLocalVariable) {
+ if (visibleLocalVariables != null) {
+ if (!visibleLocalVariables.contains(((PsiLocalVariable)psiElement).getName())) {
+ rv.set(Boolean.TRUE);
+ }
+ }
+ }
+ else if (psiElement instanceof PsiMethod) {
+ rv.set(Boolean.TRUE);
+ //final PsiMethod method = (PsiMethod)psiElement;
+ //if (!isSimpleGetter(method)) {
+ // rv.set(Boolean.TRUE);
+ //}
+ }
+ if (!rv.get().booleanValue()) {
+ super.visitReferenceExpression(expression);
+ }
+ }
+
+ @Override
+ public void visitPrefixExpression(final PsiPrefixExpression expression) {
+ final IElementType op = expression.getOperationTokenType();
+ if (JavaTokenType.PLUSPLUS.equals(op) || JavaTokenType.MINUSMINUS.equals(op)) {
+ rv.set(Boolean.TRUE);
+ }
+ else {
+ super.visitPrefixExpression(expression);
+ }
+ }
+
+ @Override
+ public void visitAssignmentExpression(final PsiAssignmentExpression expression) {
+ rv.set(Boolean.TRUE);
+ }
+
+ @Override
+ public void visitCallExpression(final PsiCallExpression callExpression) {
+ rv.set(Boolean.TRUE);
+ //final PsiMethod method = callExpression.resolveMethod();
+ //if (method == null || !isSimpleGetter(method)) {
+ // rv.set(Boolean.TRUE);
+ //}
+ //else {
+ // super.visitCallExpression(callExpression);
+ //}
+ }
+ });
+ return rv.get().booleanValue();
+ }
+
+ public abstract String findAvailableDebugAddress(boolean useSockets) throws ExecutionException;
+
+ public static boolean isSynthetic(TypeComponent typeComponent) {
+ if (typeComponent == null) {
+ return false;
+ }
+ VirtualMachine machine = typeComponent.virtualMachine();
+ return machine != null && machine.canGetSyntheticAttribute() && typeComponent.isSynthetic();
+ }
+
+ public static boolean isSimpleGetter(PsiMethod method){
+ final PsiCodeBlock body = method.getBody();
+ if(body == null){
+ return false;
+ }
+
+ final PsiStatement[] statements = body.getStatements();
+ if(statements.length != 1){
+ return false;
+ }
+
+ final PsiStatement statement = statements[0];
+ if(!(statement instanceof PsiReturnStatement)){
+ return false;
+ }
+
+ final PsiExpression value = ((PsiReturnStatement)statement).getReturnValue();
+ if(!(value instanceof PsiReferenceExpression)){
+ return false;
+ }
+
+ final PsiReferenceExpression reference = (PsiReferenceExpression)value;
+ final PsiExpression qualifier = reference.getQualifierExpression();
+ //noinspection HardCodedStringLiteral
+ if(qualifier != null && !"this".equals(qualifier.getText())) {
+ return false;
+ }
+
+ final PsiElement referent = reference.resolve();
+ if(referent == null) {
+ return false;
+ }
+
+ if(!(referent instanceof PsiField)) {
+ return false;
+ }
+
+ return ((PsiField)referent).getContainingClass().equals(method.getContainingClass());
+ }
+
+ public static boolean isPrimitiveType(final String typeName) {
+ return ourPrimitiveTypeNames.contains(typeName);
+ }
+
+ protected static class ArrayClass {
+ public String className;
+ public int dims;
+
+ public ArrayClass(String className, int dims) {
+ this.className = className;
+ this.dims = dims;
+ }
+ }
+
+ public static DebuggerUtils getInstance() {
+ return ServiceManager.getService(DebuggerUtils.class);
+ }
+
+ public abstract PsiExpression substituteThis(PsiExpression expressionWithThis, PsiExpression howToEvaluateThis, Value howToEvaluateThisValue, StackFrameContext context) throws EvaluateException;
+
+ public abstract DebuggerContext getDebuggerContext (DataContext context);
+
+ public abstract Element writeTextWithImports(TextWithImports text);
+ public abstract TextWithImports readTextWithImports (Element element);
+
+ public abstract void writeTextWithImports(Element root, @NonNls String name, TextWithImports value);
+ public abstract TextWithImports readTextWithImports (Element root, @NonNls String name);
+
+ public abstract TextWithImports createExpressionWithImports(@NonNls String expression);
+
+ public abstract PsiElement getContextElement(final StackFrameContext context);
+
+ public abstract PsiClass chooseClassDialog(String title, Project project);
+
+ public static boolean supportsJVMDebugging(FileType type) {
+ return type instanceof LanguageFileType && ((LanguageFileType)type).isJVMDebuggingSupported();
+ }
+
+ public static boolean supportsJVMDebugging(PsiFile file) {
+ final JVMDebugProvider[] providers = Extensions.getExtensions(JVMDebugProvider.EP_NAME);
+ for (JVMDebugProvider provider : providers) {
+ if (provider.supportsJVMDebugging(file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java b/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java
new file mode 100644
index 0000000..1697684
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.NoDataException;
+import com.intellij.debugger.PositionManager;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiFile;
+import com.sun.jdi.*;
+import com.sun.jdi.request.ClassPrepareRequest;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 23, 2006
+ */
+public abstract class JSR45PositionManager<Scope> implements PositionManager {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.JSR45PositionManager");
+ protected final DebugProcess myDebugProcess;
+ protected final Scope myScope;
+ private final String myStratumId;
+ protected final SourcesFinder<Scope> mySourcesFinder;
+ protected final String GENERATED_CLASS_PATTERN;
+ protected Matcher myGeneratedClassPatternMatcher;
+ private final Set<LanguageFileType> myFileTypes;
+
+ public JSR45PositionManager(DebugProcess debugProcess, Scope scope, final String stratumId, final LanguageFileType[] acceptedFileTypes,
+ final SourcesFinder<Scope> sourcesFinder) {
+ myDebugProcess = debugProcess;
+ myScope = scope;
+ myStratumId = stratumId;
+ myFileTypes = Collections.unmodifiableSet(new HashSet<LanguageFileType>(Arrays.asList(acceptedFileTypes)));
+ mySourcesFinder = sourcesFinder;
+ String generatedClassPattern = getGeneratedClassesPackage();
+ if(generatedClassPattern.length() == 0) {
+ generatedClassPattern = getGeneratedClassesNamePattern();
+ }
+ else {
+ generatedClassPattern = generatedClassPattern + "." + getGeneratedClassesNamePattern();
+ }
+ GENERATED_CLASS_PATTERN = generatedClassPattern;
+ myGeneratedClassPatternMatcher = Pattern.compile(generatedClassPattern.replaceAll("\\*", ".*")).matcher("");
+ }
+
+ @NonNls
+ protected abstract String getGeneratedClassesPackage();
+
+ protected String getGeneratedClassesNamePattern() {
+ return "*";
+ }
+
+ public final String getStratumId() {
+ return myStratumId;
+ }
+
+ public SourcePosition getSourcePosition(final Location location) throws NoDataException {
+ SourcePosition sourcePosition = null;
+
+ try {
+ String sourcePath = getRelativeSourcePathByLocation(location);
+ PsiFile file = mySourcesFinder.findSourceFile(sourcePath, myDebugProcess.getProject(), myScope);
+ if(file != null) {
+ int lineNumber = getLineNumber(location);
+ sourcePosition = SourcePosition.createFromLine(file, lineNumber - 1);
+ }
+ }
+ catch (AbsentInformationException ignored) { // ignored
+ }
+ catch (Throwable e) {
+ LOG.info(e);
+ }
+ if(sourcePosition == null) {
+ throw new NoDataException();
+ }
+ return sourcePosition;
+ }
+
+ protected String getRelativeSourcePathByLocation(final Location location) throws AbsentInformationException {
+ return getRelativePath(location.sourcePath(myStratumId));
+ }
+
+ protected int getLineNumber(final Location location) {
+ return location.lineNumber(myStratumId);
+ }
+
+ @NotNull
+ public List<ReferenceType> getAllClasses(SourcePosition classPosition) throws NoDataException {
+ checkSourcePositionFileType(classPosition);
+
+ final List<ReferenceType> referenceTypes = myDebugProcess.getVirtualMachineProxy().allClasses();
+
+ final List<ReferenceType> result = new ArrayList<ReferenceType>();
+
+ for (final ReferenceType referenceType : referenceTypes) {
+ myGeneratedClassPatternMatcher.reset(referenceType.name());
+ if (myGeneratedClassPatternMatcher.matches()) {
+ final List<Location> locations = locationsOfClassAt(referenceType, classPosition);
+ if (locations != null && locations.size() > 0) {
+ result.add(referenceType);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private void checkSourcePositionFileType(final SourcePosition classPosition) throws NoDataException {
+ final FileType fileType = classPosition.getFile().getFileType();
+ if(!myFileTypes.contains(fileType)) {
+ throw new NoDataException();
+ }
+ }
+
+ @NotNull
+ public List<Location> locationsOfLine(final ReferenceType type, final SourcePosition position) throws NoDataException {
+ List<Location> locations = locationsOfClassAt(type, position);
+ return locations != null ? locations : Collections.<Location>emptyList();
+
+ }
+
+ private List<Location> locationsOfClassAt(final ReferenceType type, final SourcePosition position) throws NoDataException {
+ checkSourcePositionFileType(position);
+
+ return ApplicationManager.getApplication().runReadAction(new Computable<List<Location>>() {
+ public List<Location> compute() {
+ try {
+ final List<String> relativePaths = getRelativeSourePathsByType(type);
+ for (String relativePath : relativePaths) {
+ final PsiFile file = mySourcesFinder.findSourceFile(relativePath, myDebugProcess.getProject(), myScope);
+ if(file != null && file.equals(position.getFile())) {
+ return getLocationsOfLine(type, getSourceName(file.getName(), type), relativePath, position.getLine() + 1);
+ }
+ }
+ }
+ catch(ObjectCollectedException ignored) {
+ }
+ catch(AbsentInformationException ignored) {
+ }
+ catch(ClassNotPreparedException ignored) {
+ }
+ catch (InternalError e) {
+ myDebugProcess.getExecutionResult().getProcessHandler().notifyTextAvailable(
+ DebuggerBundle.message("internal.error.locations.of.line", type.name()), ProcessOutputTypes.SYSTEM);
+ }
+ return null;
+ }
+
+ // Finds exact server file name (from available in type)
+ // This is needed because some servers (e.g. WebSphere) put not exact file name such as 'A.jsp '
+ private String getSourceName(final String name, final ReferenceType type) throws AbsentInformationException {
+ for(String sourceNameFromType: type.sourceNames(myStratumId)) {
+ if (sourceNameFromType.indexOf(name) >= 0) {
+ return sourceNameFromType;
+ }
+ }
+ return name;
+ }
+ });
+ }
+
+ protected List<String> getRelativeSourePathsByType(final ReferenceType type) throws AbsentInformationException {
+ final List<String> paths = type.sourcePaths(myStratumId);
+ final ArrayList<String> relativePaths = new ArrayList<String>();
+ for (String path : paths) {
+ relativePaths.add(getRelativePath(path));
+ }
+ return relativePaths;
+ }
+
+ protected List<Location> getLocationsOfLine(final ReferenceType type, final String fileName,
+ final String relativePath, final int lineNumber) throws AbsentInformationException {
+ return type.locationsOfLine(myStratumId, fileName, lineNumber);
+ }
+
+ public ClassPrepareRequest createPrepareRequest(final ClassPrepareRequestor requestor, final SourcePosition position)
+ throws NoDataException {
+ checkSourcePositionFileType(position);
+
+ return myDebugProcess.getRequestsManager().createClassPrepareRequest(new ClassPrepareRequestor() {
+ public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) {
+ onClassPrepare(debuggerProcess, referenceType, position, requestor);
+ }
+ }, GENERATED_CLASS_PATTERN);
+ }
+
+ protected void onClassPrepare(final DebugProcess debuggerProcess, final ReferenceType referenceType,
+ final SourcePosition position, final ClassPrepareRequestor requestor) {
+ try {
+ if(locationsOfClassAt(referenceType, position) != null) {
+ requestor.processClassPrepare(debuggerProcess, referenceType);
+ }
+ }
+ catch (NoDataException e) {
+ }
+ }
+
+ protected String getRelativePath(String sourcePath) {
+
+ if (sourcePath != null) {
+ sourcePath = sourcePath.trim();
+ String generatedClassesPackage = getGeneratedClassesPackage();
+ final String prefix = generatedClassesPackage.replace('.', File.separatorChar);
+
+ if (sourcePath.startsWith(prefix)) {
+ sourcePath = sourcePath.substring(prefix.length());
+ if (sourcePath.startsWith(File.separator)) {
+ sourcePath = sourcePath.substring(1);
+ }
+ }
+ }
+
+ return sourcePath;
+ }
+
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/JVMDebugProvider.java b/java/debugger/openapi/src/com/intellij/debugger/engine/JVMDebugProvider.java
new file mode 100644
index 0000000..15fde83
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/JVMDebugProvider.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.psi.PsiFile;
+
+/**
+ * @author Dennis.Ushakov
+ */
+public interface JVMDebugProvider {
+ ExtensionPointName<JVMDebugProvider> EP_NAME = ExtensionPointName.create("com.intellij.debugger.jvmDebugProvider");
+
+ boolean supportsJVMDebugging(PsiFile file);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/SourcesFinder.java b/java/debugger/openapi/src/com/intellij/debugger/engine/SourcesFinder.java
new file mode 100644
index 0000000..106499b
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/SourcesFinder.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;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: May 23, 2006
+ */
+public interface SourcesFinder<Scope> {
+ /**
+ * Searches for source file within the deployedModules
+ * @param relPath relative path of the source to be found (fetched from the class file)
+ * @param project
+ * @param scope a search scope
+ */
+ @Nullable
+ PsiFile findSourceFile(String relPath, Project project, Scope scope);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.java
new file mode 100644
index 0000000..442f2c7
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.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.engine;
+
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jun 3, 2003
+ * Time: 5:58:58 PM
+ * To change this template use Options | File Templates.
+ */
+public interface StackFrameContext {
+ @Nullable
+ StackFrameProxy getFrameProxy();
+ DebugProcess getDebugProcess();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.java
new file mode 100644
index 0000000..ae89344
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.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;
+
+import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
+
+public interface SuspendContext extends StackFrameContext {
+ public int getSuspendPolicy();
+
+ ThreadReferenceProxy getThread();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/TopLevelParentClassProvider.java b/java/debugger/openapi/src/com/intellij/debugger/engine/TopLevelParentClassProvider.java
new file mode 100644
index 0000000..782bf34
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/TopLevelParentClassProvider.java
@@ -0,0 +1,47 @@
+/*
+ * 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.engine;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Max Medvedev
+ */
+public abstract class TopLevelParentClassProvider {
+ private static final ExtensionPointName<TopLevelParentClassProvider> EP_NAME =
+ ExtensionPointName.create("com.intellij.topLevelClassProvider");
+
+ public static PsiClass getTopLevelParentClass(PsiClass psiClass) {
+ for (TopLevelParentClassProvider provider : EP_NAME.getExtensions()) {
+ final PsiClass custom = provider.getCustomTopLevelParentClass(psiClass);
+ if (custom != null) return custom;
+ }
+
+ PsiClass enclosing = PsiTreeUtil.getParentOfType(psiClass, PsiClass.class, true);
+ while (enclosing != null) {
+ psiClass = enclosing;
+ enclosing = PsiTreeUtil.getParentOfType(enclosing, PsiClass.class, true);
+ }
+ return psiClass;
+ }
+
+ @Nullable
+ protected abstract PsiClass getCustomTopLevelParentClass(PsiClass psiClass);
+
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactory.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactory.java
new file mode 100644
index 0000000..f7ceafc
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactory.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;
+
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaCodeFragment;
+import com.intellij.psi.PsiElement;
+
+public abstract class CodeFragmentFactory {
+ public static final ExtensionPointName<CodeFragmentFactory> EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.debugger.codeFragmentFactory");
+
+ public abstract JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, Project project);
+
+ public abstract JavaCodeFragment createPresentationCodeFragment(TextWithImports item, PsiElement context, Project project);
+
+ public abstract boolean isContextAccepted(PsiElement contextElement);
+
+ public abstract LanguageFileType getFileType();
+
+ /**
+ * In case if createCodeFragment returns java code use
+ * com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl#getInstance()
+ * @return builder, which can evaluate expression for your code fragment
+ */
+ public abstract EvaluatorBuilder getEvaluatorBuilder();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/CodeFragmentKind.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/CodeFragmentKind.java
new file mode 100644
index 0000000..34766d2
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/CodeFragmentKind.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+public enum CodeFragmentKind {
+ EXPRESSION, CODE_BLOCK
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluateException.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluateException.java
new file mode 100644
index 0000000..30dac08
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluateException.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.evaluation;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.ObjectReference;
+import org.jetbrains.annotations.Nullable;
+
+public class EvaluateException extends Exception {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.EvaluateException");
+ private ObjectReference myTargetException;
+
+ public EvaluateException(final String message) {
+ super(message);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(message);
+ }
+ }
+
+ public EvaluateException(String msg, Throwable th) {
+ super(msg, th);
+ if (th instanceof EvaluateException) {
+ myTargetException = ((EvaluateException)th).getExceptionFromTargetVM();
+ }
+ else if(th instanceof InvocationException){
+ InvocationException invocationException = (InvocationException) th;
+ myTargetException = invocationException.exception();
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(msg);
+ }
+ }
+
+ @Nullable
+ public ObjectReference getExceptionFromTargetVM() {
+ return myTargetException;
+ }
+
+ public void setTargetException(final ObjectReference targetException) {
+ myTargetException = targetException;
+ }
+
+ public String getMessage() {
+ final String errorMessage = super.getMessage();
+ if (errorMessage != null) {
+ return errorMessage;
+ }
+ final Throwable cause = getCause();
+ final String causeMessage = cause != null? cause.getMessage() : null;
+ if (causeMessage != null) {
+ return causeMessage;
+ }
+ return "unknown error";
+ }
+}
\ No newline at end of file
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluateExceptionUtil.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluateExceptionUtil.java
new file mode 100644
index 0000000..4d5536c
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluateExceptionUtil.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.engine.evaluation;
+
+import com.intellij.debugger.DebuggerBundle;
+import com.sun.jdi.*;
+
+/**
+ * @author lex
+ */
+public class EvaluateExceptionUtil {
+ public static final EvaluateException INCONSISTEND_DEBUG_INFO = createEvaluateException(DebuggerBundle.message("evaluation.error.inconsistent.debug.info"));
+ public static final EvaluateException BOOLEAN_EXPECTED = createEvaluateException(DebuggerBundle.message("evaluation.error.boolean.value.expected.in.condition"));
+ public static final EvaluateException PROCESS_EXITED = createEvaluateException(DebuggerBundle.message("evaluation.error.process.exited"));
+ public static final EvaluateException NULL_STACK_FRAME = createEvaluateException(DebuggerBundle.message("evaluation.error.stack.frame.unavailable"));
+ public static final EvaluateException NESTED_EVALUATION_ERROR = createEvaluateException(DebuggerBundle.message("evaluation.error.nested.evaluation"));
+ public static final EvaluateException INVALID_DEBUG_INFO = createEvaluateException(DebuggerBundle.message("evaluation.error.sources.out.of.sync"));
+ public static final EvaluateException CANNOT_FIND_SOURCE_CLASS = createEvaluateException(DebuggerBundle.message("evaluation.error.cannot.find.stackframe.source"));
+ public static final EvaluateException OBJECT_WAS_COLLECTED = createEvaluateException(DebuggerBundle.message("evaluation.error.object.collected"));
+ public static final EvaluateException ARRAY_WAS_COLLECTED = createEvaluateException(DebuggerBundle.message("evaluation.error.array.collected"));
+ public static final EvaluateException THREAD_WAS_RESUMED = createEvaluateException(DebuggerBundle.message("evaluation.error.thread.resumed"));
+ public static final EvaluateException DEBUG_INFO_UNAVAILABLE = createEvaluateException(DebuggerBundle.message("evaluation.error.debug.info.unavailable"));
+
+ private EvaluateExceptionUtil() {
+ }
+
+ public static EvaluateException createEvaluateException(Throwable th) {
+ return createEvaluateException(null, th);
+ }
+
+ public static EvaluateException createEvaluateException(String msg, Throwable th) {
+ final String message = msg != null? msg + ": " + reason(th) : reason(th);
+ return new EvaluateException(message, th instanceof EvaluateException ? th.getCause() : th);
+ }
+
+ public static EvaluateException createEvaluateException(String reason) {
+ return new EvaluateException(reason, null);
+ }
+
+ private static String reason(Throwable th) {
+ if(th instanceof InvalidTypeException) {
+ final String originalReason = th.getMessage();
+ return DebuggerBundle.message("evaluation.error.type.mismatch") + (originalReason != null? " " + originalReason : "");
+ }
+ else if(th instanceof AbsentInformationException) {
+ return DebuggerBundle.message("evaluation.error.debug.info.unavailable");
+ }
+ else if(th instanceof ClassNotLoadedException) {
+ return DebuggerBundle.message("evaluation.error.class.not.loaded", ((ClassNotLoadedException)th).className());
+ }
+ else if(th instanceof ClassNotPreparedException) {
+ return th.getMessage();
+ }
+ else if(th instanceof IncompatibleThreadStateException) {
+ return DebuggerBundle.message("evaluation.error.thread.not.at.breakpoint");
+ }
+ else if(th instanceof InconsistentDebugInfoException) {
+ return DebuggerBundle.message("evaluation.error.inconsistent.debug.info");
+ }
+ else if(th instanceof ObjectCollectedException) {
+ return DebuggerBundle.message("evaluation.error.object.collected");
+ }
+ else if(th instanceof InvocationException){
+ InvocationException invocationException = (InvocationException) th;
+ return DebuggerBundle.message("evaluation.error.method.exception", invocationException.exception().referenceType().name());
+ }
+ else if(th instanceof EvaluateException) {
+ return th.getMessage();
+ }
+ else {
+ return th.getClass().getName() + " : " + (th.getMessage() != null ? th.getMessage() : "");
+ }
+ }
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java
new file mode 100644
index 0000000..e50ef43
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.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.engine.evaluation;
+
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.StackFrameContext;
+import com.intellij.debugger.engine.SuspendContext;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ClassLoaderReference;
+import com.sun.jdi.Value;
+
+public interface EvaluationContext extends StackFrameContext{
+ DebugProcess getDebugProcess();
+
+ EvaluationContext createEvaluationContext(Value value);
+
+ SuspendContext getSuspendContext();
+
+ Project getProject();
+
+ ClassLoaderReference getClassLoader() throws EvaluateException;
+
+ Value getThisObject();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/TextWithImports.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/TextWithImports.java
new file mode 100644
index 0000000..0ae3e64
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/TextWithImports.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.
+ */
+package com.intellij.debugger.engine.evaluation;
+
+import com.intellij.openapi.fileTypes.FileType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface TextWithImports {
+ String getText();
+
+ void setText(String newText);
+
+ @NotNull
+ String getImports();
+
+ CodeFragmentKind getKind();
+
+ boolean isEmpty();
+
+ String toExternalForm();
+
+ @Nullable
+ FileType getFileType();
+
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilder.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilder.java
new file mode 100644
index 0000000..66bb45a
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * 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.engine.evaluation.expression;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImports;
+import com.intellij.psi.PsiElement;
+
+/**
+ * Main interface to extend evaluation for different JVM languages.
+ * @see com.intellij.debugger.engine.evaluation.CodeFragmentFactory
+ */
+public interface EvaluatorBuilder {
+ ExpressionEvaluator build(PsiElement codeFragment, final SourcePosition position) throws EvaluateException;
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluator.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluator.java
new file mode 100644
index 0000000..18528c2
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluator.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.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+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 interface ExpressionEvaluator {
+ //call evaluate before
+ public Value getValue();
+
+ //call evaluate before
+ public Modifier getModifier();
+
+ public Value evaluate(final EvaluationContext context) throws EvaluateException;
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/Modifier.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/Modifier.java
new file mode 100644
index 0000000..809feac
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/expression/Modifier.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.
+ */
+
+/*
+ * Interface Modifier
+ * @author Jeka
+ */
+package com.intellij.debugger.engine.evaluation.expression;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+
+public interface Modifier {
+ boolean canInspect();
+
+ boolean canSetValue();
+ /**
+ * sets the value to the expression
+ */
+ void setValue(Value value) throws ClassNotLoadedException, InvalidTypeException, EvaluateException;
+
+ /**
+ * @return the expected type of the expression or null is class was not loaded
+ */
+ Type getExpectedType() throws ClassNotLoadedException, EvaluateException;
+
+ NodeDescriptor getInspectItem(Project project);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/LocalVariableProxy.java b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/LocalVariableProxy.java
new file mode 100644
index 0000000..8efdaae
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/LocalVariableProxy.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.jdi;
+
+public interface LocalVariableProxy extends ObjectReferenceProxy{
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ObjectReferenceProxy.java b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ObjectReferenceProxy.java
new file mode 100644
index 0000000..3ec95fb
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ObjectReferenceProxy.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.jdi;
+
+public interface ObjectReferenceProxy {
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/StackFrameProxy.java b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/StackFrameProxy.java
new file mode 100644
index 0000000..7e0b6ee
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/StackFrameProxy.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.engine.jdi;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.sun.jdi.ClassLoaderReference;
+import com.sun.jdi.Location;
+import com.sun.jdi.StackFrame;
+
+public interface StackFrameProxy extends ObjectReferenceProxy{
+ StackFrame getStackFrame() throws EvaluateException;
+
+ int getFrameIndex() throws EvaluateException ;
+
+ VirtualMachineProxy getVirtualMachine();
+
+ Location location() throws EvaluateException;
+
+ ClassLoaderReference getClassLoader() throws EvaluateException;
+
+ LocalVariableProxy visibleVariableByName(String name) throws EvaluateException;
+
+ ThreadReferenceProxy threadProxy();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ThreadGroupReferenceProxy.java b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ThreadGroupReferenceProxy.java
new file mode 100644
index 0000000..146ab75
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ThreadGroupReferenceProxy.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.jdi;
+
+import com.sun.jdi.ThreadGroupReference;
+
+public interface ThreadGroupReferenceProxy extends ObjectReferenceProxy{
+ ThreadGroupReference getThreadGroupReference();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ThreadReferenceProxy.java b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ThreadReferenceProxy.java
new file mode 100644
index 0000000..9723516
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/ThreadReferenceProxy.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.jdi;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.sun.jdi.ThreadReference;
+
+public interface ThreadReferenceProxy extends ObjectReferenceProxy{
+ VirtualMachineProxy getVirtualMachine();
+ ThreadReference getThreadReference();
+
+ StackFrameProxy frame(int i) throws EvaluateException;
+
+ int frameCount() throws EvaluateException;
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/VirtualMachineProxy.java b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/VirtualMachineProxy.java
new file mode 100644
index 0000000..4f4cb82
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/jdi/VirtualMachineProxy.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.jdi;
+
+import com.intellij.debugger.engine.DebugProcess;
+import com.sun.jdi.ReferenceType;
+
+import java.util.List;
+
+/**
+ * @author lex
+ */
+public interface VirtualMachineProxy {
+ List<ReferenceType> allClasses();
+
+ boolean versionHigher(String version);
+
+ boolean canWatchFieldModification();
+
+ boolean canWatchFieldAccess();
+
+ boolean canInvokeMethods();
+
+ DebugProcess getDebugProcess();
+
+ List<ReferenceType> nestedTypes(ReferenceType refType);
+
+ List<ReferenceType> classesByName(String s);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/DebuggerCommand.java b/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/DebuggerCommand.java
new file mode 100644
index 0000000..a39d83f
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/DebuggerCommand.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.managerThread;
+
+public interface DebuggerCommand {
+ void action();
+
+ void commandCancelled();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/DebuggerManagerThread.java b/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/DebuggerManagerThread.java
new file mode 100644
index 0000000..8d19667
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/DebuggerManagerThread.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.engine.managerThread;
+
+public interface DebuggerManagerThread {
+ /**
+ * executes command in DebuggerManagerThread
+ *
+ * @param command
+ */
+ void invokeCommand(DebuggerCommand command);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/SuspendContextCommand.java b/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/SuspendContextCommand.java
new file mode 100644
index 0000000..13d75fe
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/engine/managerThread/SuspendContextCommand.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.managerThread;
+
+import com.intellij.debugger.engine.SuspendContext;
+
+public interface SuspendContextCommand extends DebuggerCommand{
+ SuspendContext getSuspendContext();
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/package.html b/java/debugger/openapi/src/com/intellij/debugger/package.html
new file mode 100644
index 0000000..e9fa628
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/package.html
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright 2000-2007 JetBrains s.r.o.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html><body bgcolor="white">
+Provides interfaces for interacting with IDEA's debugger. The main extensibility
+point of the debugger is the possibility for a plugin to provide custom mapping of
+positions in compiled classes to source code lines, which is mostly necessary for
+JSP debugging. Such mapping is provided through the {@link PositionManager} interface.
+One standard implementation of this interface for application servers compatible
+with the JSR-45 specification is provided by the {@link JSR45PositionManager} class.
+Another implementation of this interface, which can be used as an example for the debugger
+API, is found in the Tomcat plugin.
+</body></html>
diff --git a/java/debugger/openapi/src/com/intellij/debugger/requests/ClassPrepareRequestor.java b/java/debugger/openapi/src/com/intellij/debugger/requests/ClassPrepareRequestor.java
new file mode 100644
index 0000000..e58e052
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/requests/ClassPrepareRequestor.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.requests;
+
+import com.intellij.debugger.engine.DebugProcess;
+import com.sun.jdi.ReferenceType;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jun 27, 2003
+ * Time: 7:27:41 PM
+ * To change this template use Options | File Templates.
+ */
+public interface ClassPrepareRequestor extends Requestor {
+ void processClassPrepare(DebugProcess debuggerProcess, final ReferenceType referenceType);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/requests/RequestManager.java b/java/debugger/openapi/src/com/intellij/debugger/requests/RequestManager.java
new file mode 100644
index 0000000..bd949fd
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/requests/RequestManager.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.requests;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.sun.jdi.request.ClassPrepareRequest;
+import com.sun.jdi.request.EventRequest;
+
+public interface RequestManager {
+ void callbackOnPrepareClasses(ClassPrepareRequestor requestor, String classOrPatternToBeLoaded);
+ void callbackOnPrepareClasses(ClassPrepareRequestor requestor, SourcePosition classPosition) throws EvaluateException;
+
+ ClassPrepareRequest createClassPrepareRequest(ClassPrepareRequestor requestor, String pattern);
+
+ void enableRequest(EventRequest request);
+
+ void setInvalid(Requestor requestor, String message);
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/requests/Requestor.java b/java/debugger/openapi/src/com/intellij/debugger/requests/Requestor.java
new file mode 100644
index 0000000..9a13832
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/requests/Requestor.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.requests;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: lex
+ * Date: Jun 27, 2003
+ * Time: 8:06:36 PM
+ * To change this template use Options | File Templates.
+ */
+public interface Requestor {
+}
diff --git a/java/debugger/openapi/src/com/intellij/debugger/ui/tree/NodeDescriptor.java b/java/debugger/openapi/src/com/intellij/debugger/ui/tree/NodeDescriptor.java
new file mode 100644
index 0000000..4ed6de6
--- /dev/null
+++ b/java/debugger/openapi/src/com/intellij/debugger/ui/tree/NodeDescriptor.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.util.Key;
+
+public interface NodeDescriptor {
+
+ String getName();
+ String getLabel();
+
+ <T> T getUserData(Key<T> key);
+ <T> void putUserData(Key<T> key, T value);
+
+ void displayAs(NodeDescriptor descriptor);
+
+ void setAncestor(NodeDescriptor oldDescriptor);
+}