2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.debugger
.actions
;
18 import com
.intellij
.debugger
.DebuggerBundle
;
19 import com
.intellij
.debugger
.DebuggerInvocationUtil
;
20 import com
.intellij
.debugger
.DebuggerManagerEx
;
21 import com
.intellij
.debugger
.engine
.ContextUtil
;
22 import com
.intellij
.debugger
.engine
.evaluation
.*;
23 import com
.intellij
.debugger
.engine
.evaluation
.expression
.EvaluatorBuilderImpl
;
24 import com
.intellij
.debugger
.engine
.evaluation
.expression
.ExpressionEvaluator
;
25 import com
.intellij
.debugger
.engine
.evaluation
.expression
.Modifier
;
26 import com
.intellij
.debugger
.engine
.events
.DebuggerContextCommandImpl
;
27 import com
.intellij
.debugger
.engine
.events
.SuspendContextCommandImpl
;
28 import com
.intellij
.debugger
.impl
.*;
29 import com
.intellij
.debugger
.jdi
.LocalVariableProxyImpl
;
30 import com
.intellij
.debugger
.jdi
.VirtualMachineProxyImpl
;
31 import com
.intellij
.debugger
.ui
.DebuggerExpressionComboBox
;
32 import com
.intellij
.debugger
.ui
.EditorEvaluationCommand
;
33 import com
.intellij
.debugger
.ui
.impl
.DebuggerTreeRenderer
;
34 import com
.intellij
.debugger
.ui
.impl
.watch
.*;
35 import com
.intellij
.debugger
.ui
.tree
.render
.HexRenderer
;
36 import com
.intellij
.debugger
.ui
.tree
.render
.NodeRenderer
;
37 import com
.intellij
.debugger
.ui
.tree
.render
.ValueLabelRenderer
;
38 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
39 import com
.intellij
.openapi
.editor
.Editor
;
40 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
41 import com
.intellij
.openapi
.progress
.util
.ProgressIndicatorListenerAdapter
;
42 import com
.intellij
.openapi
.progress
.util
.ProgressWindowWithNotification
;
43 import com
.intellij
.openapi
.project
.Project
;
44 import com
.intellij
.openapi
.ui
.Messages
;
45 import com
.intellij
.psi
.PsiDocumentManager
;
46 import com
.intellij
.psi
.PsiFile
;
47 import com
.intellij
.ui
.SimpleColoredComponent
;
48 import com
.intellij
.util
.IJSwingUtilities
;
54 * Class SetValueAction
57 public class SetValueAction
extends DebuggerAction
{
58 public void update(AnActionEvent e
) {
59 boolean enable
= false;
60 DebuggerTreeNodeImpl node
= getSelectedNode(e
.getDataContext());
62 NodeDescriptorImpl descriptor
= node
.getDescriptor();
63 if(descriptor
instanceof ValueDescriptorImpl
){
64 ValueDescriptorImpl valueDescriptor
= ((ValueDescriptorImpl
)descriptor
);
65 enable
= valueDescriptor
.canSetValue();
68 e
.getPresentation().setVisible(enable
);
71 private void update(final DebuggerContextImpl context
) {
72 DebuggerInvocationUtil
.swingInvokeLater(context
.getProject(), new Runnable() {
74 context
.getDebuggerSession().refresh(false);
77 //node.setState(context);
80 public void actionPerformed(final AnActionEvent event
) {
81 final DebuggerTreeNodeImpl node
= getSelectedNode(event
.getDataContext());
85 final NodeDescriptorImpl descriptor
= node
.getDescriptor();
86 if (!(descriptor
instanceof ValueDescriptorImpl
)) {
89 if(!((ValueDescriptorImpl
)descriptor
).canSetValue()) {
93 final DebuggerTree tree
= getTree(event
.getDataContext());
94 final DebuggerContextImpl debuggerContext
= getDebuggerContext(event
.getDataContext());
97 if (descriptor
instanceof FieldDescriptorImpl
) {
98 FieldDescriptorImpl fieldDescriptor
= (FieldDescriptorImpl
)descriptor
;
99 final Field field
= fieldDescriptor
.getField();
100 if (!field
.isStatic()) {
101 final ObjectReference object
= fieldDescriptor
.getObject();
102 if (object
!= null) {
103 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
104 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
105 object
.setValue(field
, preprocessValue(evaluationContext
, newValue
, field
.type()));
106 update(debuggerContext
);
109 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
110 ClassNotLoadedException
,
111 IncompatibleThreadStateException
,
112 InvalidTypeException
,
114 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
, field
.declaringType().classLoader());
121 ReferenceType refType
= field
.declaringType();
122 if (refType
instanceof ClassType
) {
123 final ClassType classType
= (ClassType
)refType
;
124 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
125 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
126 classType
.setValue(field
, preprocessValue(evaluationContext
, newValue
, field
.type()));
127 update(debuggerContext
);
130 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
131 ClassNotLoadedException
,
132 IncompatibleThreadStateException
,
133 InvalidTypeException
,
135 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
,
136 field
.declaringType().classLoader());
142 else if (descriptor
instanceof LocalVariableDescriptorImpl
) {
143 LocalVariableDescriptorImpl localDescriptor
= (LocalVariableDescriptorImpl
)descriptor
;
144 final LocalVariableProxyImpl local
= localDescriptor
.getLocalVariable();
146 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
147 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
,
148 InvalidTypeException
,
150 debuggerContext
.getFrameProxy().setValue(local
, preprocessValue(evaluationContext
, newValue
, local
.getType()));
151 update(debuggerContext
);
154 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
155 ClassNotLoadedException
,
156 IncompatibleThreadStateException
,
157 InvalidTypeException
,
159 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
,
160 evaluationContext
.getClassLoader());
165 else if (descriptor
instanceof ArrayElementDescriptorImpl
) {
166 final ArrayElementDescriptorImpl elementDescriptor
= (ArrayElementDescriptorImpl
)descriptor
;
167 final ArrayReference array
= elementDescriptor
.getArray();
169 if (VirtualMachineProxyImpl
.isCollected(array
)) {
170 // will only be the case if debugger does not use ObjectReference.disableCollection() because of Patches.IBM_JDK_DISABLE_COLLECTION_BUG
171 Messages
.showWarningDialog(tree
, DebuggerBundle
.message("evaluation.error.array.collected") + "\n"+ DebuggerBundle
.message("warning.recalculate"), DebuggerBundle
.message("title.set.value"));
172 node
.getParent().calcValue();
175 final ArrayType arrType
= (ArrayType
)array
.referenceType();
176 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
177 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
178 array
.setValue(elementDescriptor
.getIndex(), preprocessValue(evaluationContext
, newValue
, arrType
.componentType()));
179 update(debuggerContext
);
182 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
183 ClassNotLoadedException
,
184 IncompatibleThreadStateException
,
185 InvalidTypeException
,
187 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
, arrType
.classLoader());
192 else if (descriptor
instanceof EvaluationDescriptor
) {
193 final EvaluationDescriptor evaluationDescriptor
= (EvaluationDescriptor
)descriptor
;
194 if (evaluationDescriptor
.canSetValue()) {
195 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
196 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
197 final Modifier modifier
= evaluationDescriptor
.getModifier();
198 modifier
.setValue(preprocessValue(evaluationContext
, newValue
, modifier
.getExpectedType()));
199 update(debuggerContext
);
202 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
203 ClassNotLoadedException
,
204 IncompatibleThreadStateException
,
205 InvalidTypeException
,
207 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
,
208 evaluationContext
.getClassLoader());
215 private Value
preprocessValue(EvaluationContextImpl context
, Value value
, Type varType
) throws EvaluateException
{
216 if (value
!= null && "java.lang.String".equals(varType
.name()) && !(value
instanceof StringReference
)) {
217 String v
= DebuggerUtilsEx
.getValueAsString(context
, value
);
219 value
= context
.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf(v
);
222 if(value
instanceof DoubleValue
) {
223 double dValue
= ((DoubleValue
) value
).doubleValue();
224 if(varType
instanceof FloatType
&& Float
.MIN_VALUE
<= dValue
&& dValue
<= Float
.MAX_VALUE
){
225 value
= context
.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf((float)dValue
);
231 private static interface SetValueRunnable
{
232 void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
,
233 InvalidTypeException
,
235 IncompatibleThreadStateException
;
236 ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws EvaluateException
,
238 ClassNotLoadedException
,
239 IncompatibleThreadStateException
,
240 InvalidTypeException
;
243 private static void setValue(String expressionToShow
, ExpressionEvaluator evaluator
, EvaluationContextImpl evaluationContext
, SetValueRunnable setValueRunnable
) throws EvaluateException
{
246 value
= evaluator
.evaluate(evaluationContext
);
248 setValueRunnable
.setValue(evaluationContext
, value
);
250 catch (IllegalArgumentException ex
) {
251 throw EvaluateExceptionUtil
.createEvaluateException(ex
.getMessage());
253 catch (InvalidTypeException ex
) {
254 throw EvaluateExceptionUtil
.createEvaluateException(DebuggerBundle
.message("evaluation.error.type.mismatch"));
256 catch (IncompatibleThreadStateException e
) {
257 throw EvaluateExceptionUtil
.createEvaluateException(e
);
259 catch (ClassNotLoadedException ex
) {
260 if (!evaluationContext
.isAutoLoadClasses()) {
261 throw EvaluateExceptionUtil
.createEvaluateException(ex
);
263 final ReferenceType refType
;
265 refType
= setValueRunnable
.loadClass(evaluationContext
, ex
.className());
266 if (refType
!= null) {
268 setValue(expressionToShow
, evaluator
, evaluationContext
, setValueRunnable
);
271 catch (InvocationException e
) {
272 throw EvaluateExceptionUtil
.createEvaluateException(e
);
274 catch (ClassNotLoadedException e
) {
275 throw EvaluateExceptionUtil
.createEvaluateException(e
);
277 catch (IncompatibleThreadStateException e
) {
278 throw EvaluateExceptionUtil
.createEvaluateException(e
);
280 catch (InvalidTypeException e
) {
281 throw EvaluateExceptionUtil
.createEvaluateException(e
);
283 catch (ObjectCollectedException e
) {
284 throw EvaluateExceptionUtil
.OBJECT_WAS_COLLECTED
;
289 private void askAndSet(final DebuggerTreeNodeImpl node
, final DebuggerContextImpl debuggerContext
, final SetValueRunnable setValueRunnable
) {
290 ProgressWindowWithNotification progressWindow
= new ProgressWindowWithNotification(true, debuggerContext
.getProject());
292 SuspendContextCommandImpl askSetAction
= new DebuggerContextCommandImpl(debuggerContext
) {
293 public Priority
getPriority() {
294 return Priority
.HIGH
;
297 public void threadAction() {
298 final NodeDescriptorImpl descriptor
= node
.getDescriptor();
299 String initialString
= "";
300 if (descriptor
instanceof ValueDescriptorImpl
) {
301 Value currentValue
= ((ValueDescriptorImpl
) descriptor
).getValue();
302 if (currentValue
instanceof StringReference
) {
303 initialString
= DebuggerUtilsEx
.getValueOrErrorAsString(debuggerContext
.createEvaluationContext(), currentValue
);
304 initialString
= initialString
== null ?
"" : "\"" + DebuggerUtilsEx
.translateStringValue(initialString
) + "\"";
306 else if (currentValue
instanceof PrimitiveValue
) {
307 ValueLabelRenderer renderer
= ((ValueDescriptorImpl
) descriptor
).getRenderer(debuggerContext
.getDebugProcess());
308 initialString
= getDisplayableString((PrimitiveValue
) currentValue
, renderer
instanceof NodeRenderer
&& HexRenderer
.UNIQUE_ID
.equals(renderer
.getUniqueId()));
311 final String initialString1
= initialString
;
312 final Project project
= debuggerContext
.getProject();
313 DebuggerInvocationUtil
.swingInvokeLater(project
, new Runnable() {
315 showEditor(new TextWithImportsImpl(CodeFragmentKind
.EXPRESSION
, initialString1
), node
, debuggerContext
, setValueRunnable
);
322 progressWindow
.setTitle(DebuggerBundle
.message("title.evaluating"));
323 debuggerContext
.getDebugProcess().getManagerThread().startProgress(askSetAction
, progressWindow
);
326 private void showEditor(final TextWithImports initialString
,
327 final DebuggerTreeNodeImpl node
,
328 final DebuggerContextImpl debuggerContext
,
329 final SetValueRunnable setValueRunnable
) {
330 final JPanel editorPanel
= new JPanel();
331 editorPanel
.setLayout(new BoxLayout(editorPanel
, BoxLayout
.X_AXIS
));
332 SimpleColoredComponent label
= new SimpleColoredComponent();
333 label
.setIcon(node
.getIcon());
334 DebuggerTreeRenderer
.getDescriptorTitle(debuggerContext
, node
.getDescriptor()).appendToComponent(label
);
335 editorPanel
.add(label
);
337 final DebuggerExpressionComboBox comboBox
= new DebuggerExpressionComboBox(
338 debuggerContext
.getProject(),
339 PositionUtil
.getContextElement(debuggerContext
),
340 "setValue", DefaultCodeFragmentFactory
.getInstance());
341 comboBox
.setText(initialString
);
342 comboBox
.selectAll();
343 editorPanel
.add(comboBox
);
345 final DebuggerTreeInplaceEditor editor
= new DebuggerTreeInplaceEditor(node
) {
346 public JComponent
createInplaceEditorComponent() {
350 public JComponent
getPreferredFocusedComponent() {
354 public Editor
getEditor() {
355 return comboBox
.getEditor();
358 public JComponent
getEditorComponent() {
359 return comboBox
.getEditorComponent();
362 private void flushValue() {
363 Editor editor
= getEditor();
368 final TextWithImports text
= comboBox
.getText();
370 PsiFile psiFile
= PsiDocumentManager
.getInstance(debuggerContext
.getProject()).getPsiFile(editor
.getDocument());
372 final ProgressWindowWithNotification progressWindow
= new ProgressWindowWithNotification(true, getProject());
373 EditorEvaluationCommand evaluationCommand
= new EditorEvaluationCommand(getEditor(), psiFile
, debuggerContext
, progressWindow
) {
374 public void threadAction() {
378 catch(EvaluateException e
) {
379 progressWindow
.cancel();
381 catch(ProcessCanceledException e
) {
382 progressWindow
.cancel();
385 if (!progressWindow
.isCanceled()) {
386 DebuggerInvocationUtil
.swingInvokeLater(debuggerContext
.getProject(), new Runnable() {
388 comboBox
.addRecent(text
);
396 protected Object
evaluate(final EvaluationContextImpl evaluationContext
) throws EvaluateException
{
397 ExpressionEvaluator evaluator
= DebuggerInvocationUtil
.commitAndRunReadAction(evaluationContext
.getProject(), new com
.intellij
.debugger
.EvaluatingComputable
<ExpressionEvaluator
>() {
398 public ExpressionEvaluator
compute() throws EvaluateException
{
399 return EvaluatorBuilderImpl
.getInstance().build(text
, ContextUtil
.getContextElement(evaluationContext
), ContextUtil
.getSourcePosition(evaluationContext
));
403 SetValueAction
.setValue(text
.getText(), evaluator
, evaluationContext
, new SetValueRunnable() {
404 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
,
405 InvalidTypeException
,
407 IncompatibleThreadStateException
{
408 if(!progressWindow
.isCanceled()) {
409 setValueRunnable
.setValue(evaluationContext
, newValue
);
414 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
415 ClassNotLoadedException
,
417 IncompatibleThreadStateException
,
418 InvalidTypeException
{
419 return setValueRunnable
.loadClass(evaluationContext
, className
);
427 progressWindow
.addListener(new ProgressIndicatorListenerAdapter() {
428 //should return whether to stop processing
429 public void stopped() {
430 if(!progressWindow
.isCanceled()) {
431 IJSwingUtilities
.invoke(new Runnable() {
442 progressWindow
.setTitle(DebuggerBundle
.message("progress.set.value"));
443 debuggerContext
.getDebugProcess().getManagerThread().startProgress(evaluationCommand
, progressWindow
);
446 public void cancelEditing() {
448 super.cancelEditing();
455 public void doOKAction() {
466 final DebuggerStateManager stateManager
= DebuggerManagerEx
.getInstanceEx(debuggerContext
.getProject()).getContextManager();
468 stateManager
.addListener(new DebuggerContextListener() {
469 public void changeEvent(DebuggerContextImpl newContext
, int event
) {
470 stateManager
.removeListener(this);
471 editor
.cancelEditing();
475 node
.getTree().hideTooltip();
480 @SuppressWarnings({"HardCodedStringLiteral"})
481 private static String
getDisplayableString(PrimitiveValue value
, boolean showAsHex
) {
482 if (value
instanceof CharValue
) {
483 long longValue
= value
.longValue();
484 return showAsHex ?
"0x" + Long
.toHexString(longValue
).toUpperCase() : Long
.toString(longValue
);
486 if (value
instanceof ByteValue
) {
487 byte val
= value
.byteValue();
488 String strValue
= Integer
.toHexString(val
).toUpperCase();
489 if (strValue
.length() > 2) {
490 strValue
= strValue
.substring(strValue
.length() - 2);
492 return showAsHex ?
"0x" + strValue
: value
.toString();
494 if (value
instanceof ShortValue
) {
495 short val
= value
.shortValue();
496 String strValue
= Integer
.toHexString(val
).toUpperCase();
497 if (strValue
.length() > 4) {
498 strValue
= strValue
.substring(strValue
.length() - 4);
500 return showAsHex ?
"0x" + strValue
: value
.toString();
502 if (value
instanceof IntegerValue
) {
503 int val
= value
.intValue();
504 return showAsHex ?
"0x" + Integer
.toHexString(val
).toUpperCase() : value
.toString();
506 if (value
instanceof LongValue
) {
507 long val
= value
.longValue();
508 return showAsHex ?
"0x" + Long
.toHexString(val
).toUpperCase() + "L" : value
.toString() + "L";
510 return DebuggerUtilsEx
.translateStringValue(value
.toString());