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
.*;
24 import com
.intellij
.debugger
.engine
.events
.DebuggerContextCommandImpl
;
25 import com
.intellij
.debugger
.engine
.events
.SuspendContextCommandImpl
;
26 import com
.intellij
.debugger
.impl
.*;
27 import com
.intellij
.debugger
.jdi
.LocalVariableProxyImpl
;
28 import com
.intellij
.debugger
.jdi
.VirtualMachineProxyImpl
;
29 import com
.intellij
.debugger
.ui
.DebuggerExpressionComboBox
;
30 import com
.intellij
.debugger
.ui
.EditorEvaluationCommand
;
31 import com
.intellij
.debugger
.ui
.impl
.DebuggerTreeRenderer
;
32 import com
.intellij
.debugger
.ui
.impl
.watch
.*;
33 import com
.intellij
.debugger
.ui
.tree
.render
.HexRenderer
;
34 import com
.intellij
.debugger
.ui
.tree
.render
.NodeRenderer
;
35 import com
.intellij
.debugger
.ui
.tree
.render
.ValueLabelRenderer
;
36 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
37 import com
.intellij
.openapi
.editor
.Editor
;
38 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
39 import com
.intellij
.openapi
.progress
.util
.ProgressIndicatorListenerAdapter
;
40 import com
.intellij
.openapi
.progress
.util
.ProgressWindowWithNotification
;
41 import com
.intellij
.openapi
.project
.Project
;
42 import com
.intellij
.openapi
.ui
.Messages
;
43 import com
.intellij
.psi
.PsiDocumentManager
;
44 import com
.intellij
.psi
.PsiFile
;
45 import com
.intellij
.ui
.SimpleColoredComponent
;
46 import com
.intellij
.util
.IJSwingUtilities
;
52 * Class SetValueAction
55 public class SetValueAction
extends DebuggerAction
{
56 public void update(AnActionEvent e
) {
57 boolean enable
= false;
58 DebuggerTreeNodeImpl node
= getSelectedNode(e
.getDataContext());
60 NodeDescriptorImpl descriptor
= node
.getDescriptor();
61 if(descriptor
instanceof ValueDescriptorImpl
){
62 ValueDescriptorImpl valueDescriptor
= ((ValueDescriptorImpl
)descriptor
);
63 enable
= valueDescriptor
.canSetValue();
66 e
.getPresentation().setVisible(enable
);
69 private void update(final DebuggerContextImpl context
) {
70 DebuggerInvocationUtil
.swingInvokeLater(context
.getProject(), new Runnable() {
72 context
.getDebuggerSession().refresh(false);
75 //node.setState(context);
78 public void actionPerformed(final AnActionEvent event
) {
79 final DebuggerTreeNodeImpl node
= getSelectedNode(event
.getDataContext());
83 final NodeDescriptorImpl descriptor
= node
.getDescriptor();
84 if (!(descriptor
instanceof ValueDescriptorImpl
)) {
87 if(!((ValueDescriptorImpl
)descriptor
).canSetValue()) {
91 final DebuggerTree tree
= getTree(event
.getDataContext());
92 final DebuggerContextImpl debuggerContext
= getDebuggerContext(event
.getDataContext());
95 if (descriptor
instanceof FieldDescriptorImpl
) {
96 FieldDescriptorImpl fieldDescriptor
= (FieldDescriptorImpl
)descriptor
;
97 final Field field
= fieldDescriptor
.getField();
98 if (!field
.isStatic()) {
99 final ObjectReference object
= fieldDescriptor
.getObject();
100 if (object
!= null) {
101 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
102 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
103 object
.setValue(field
, preprocessValue(evaluationContext
, newValue
, field
.type()));
104 update(debuggerContext
);
107 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
108 ClassNotLoadedException
,
109 IncompatibleThreadStateException
,
110 InvalidTypeException
,
112 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
, field
.declaringType().classLoader());
119 ReferenceType refType
= field
.declaringType();
120 if (refType
instanceof ClassType
) {
121 final ClassType classType
= (ClassType
)refType
;
122 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
123 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
124 classType
.setValue(field
, preprocessValue(evaluationContext
, newValue
, field
.type()));
125 update(debuggerContext
);
128 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
129 ClassNotLoadedException
,
130 IncompatibleThreadStateException
,
131 InvalidTypeException
,
133 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
,
134 field
.declaringType().classLoader());
140 else if (descriptor
instanceof LocalVariableDescriptorImpl
) {
141 LocalVariableDescriptorImpl localDescriptor
= (LocalVariableDescriptorImpl
)descriptor
;
142 final LocalVariableProxyImpl local
= localDescriptor
.getLocalVariable();
144 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
145 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
,
146 InvalidTypeException
,
148 debuggerContext
.getFrameProxy().setValue(local
, preprocessValue(evaluationContext
, newValue
, local
.getType()));
149 update(debuggerContext
);
152 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
153 ClassNotLoadedException
,
154 IncompatibleThreadStateException
,
155 InvalidTypeException
,
157 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
,
158 evaluationContext
.getClassLoader());
163 else if (descriptor
instanceof ArrayElementDescriptorImpl
) {
164 final ArrayElementDescriptorImpl elementDescriptor
= (ArrayElementDescriptorImpl
)descriptor
;
165 final ArrayReference array
= elementDescriptor
.getArray();
167 if (VirtualMachineProxyImpl
.isCollected(array
)) {
168 // will only be the case if debugger does not use ObjectReference.disableCollection() because of Patches.IBM_JDK_DISABLE_COLLECTION_BUG
169 Messages
.showWarningDialog(tree
, DebuggerBundle
.message("evaluation.error.array.collected") + "\n"+ DebuggerBundle
.message("warning.recalculate"), DebuggerBundle
.message("title.set.value"));
170 node
.getParent().calcValue();
173 final ArrayType arrType
= (ArrayType
)array
.referenceType();
174 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
175 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
176 array
.setValue(elementDescriptor
.getIndex(), preprocessValue(evaluationContext
, newValue
, arrType
.componentType()));
177 update(debuggerContext
);
180 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
181 ClassNotLoadedException
,
182 IncompatibleThreadStateException
,
183 InvalidTypeException
,
185 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
, arrType
.classLoader());
190 else if (descriptor
instanceof EvaluationDescriptor
) {
191 final EvaluationDescriptor evaluationDescriptor
= (EvaluationDescriptor
)descriptor
;
192 if (evaluationDescriptor
.canSetValue()) {
193 askAndSet(node
, debuggerContext
, new SetValueRunnable() {
194 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
, InvalidTypeException
, EvaluateException
{
195 final Modifier modifier
= evaluationDescriptor
.getModifier();
196 modifier
.setValue(preprocessValue(evaluationContext
, newValue
, modifier
.getExpectedType()));
197 update(debuggerContext
);
200 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
201 ClassNotLoadedException
,
202 IncompatibleThreadStateException
,
203 InvalidTypeException
,
205 return evaluationContext
.getDebugProcess().loadClass(evaluationContext
, className
,
206 evaluationContext
.getClassLoader());
213 private Value
preprocessValue(EvaluationContextImpl context
, Value value
, Type varType
) throws EvaluateException
{
214 if (value
!= null && "java.lang.String".equals(varType
.name()) && !(value
instanceof StringReference
)) {
215 String v
= DebuggerUtilsEx
.getValueAsString(context
, value
);
217 value
= context
.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf(v
);
220 if(value
instanceof DoubleValue
) {
221 double dValue
= ((DoubleValue
) value
).doubleValue();
222 if(varType
instanceof FloatType
&& Float
.MIN_VALUE
<= dValue
&& dValue
<= Float
.MAX_VALUE
){
223 value
= context
.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf((float)dValue
);
227 if (varType
instanceof PrimitiveType
) {
228 if (!(value
instanceof PrimitiveValue
)) {
229 value
= (Value
)new UnBoxingEvaluator(new IdentityEvaluator(value
)).evaluate(context
);
232 else if (UnBoxingEvaluator
.isTypeUnboxable(varType
.name())) {
233 // variable is not primitive and boxing/unboxing is applicable
234 if (value
instanceof PrimitiveValue
) {
235 value
= (Value
)new BoxingEvaluator(new IdentityEvaluator(value
)).evaluate(context
);
242 private static interface SetValueRunnable
{
243 void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
,
244 InvalidTypeException
,
246 IncompatibleThreadStateException
;
247 ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws EvaluateException
,
249 ClassNotLoadedException
,
250 IncompatibleThreadStateException
,
251 InvalidTypeException
;
254 private static void setValue(String expressionToShow
, ExpressionEvaluator evaluator
, EvaluationContextImpl evaluationContext
, SetValueRunnable setValueRunnable
) throws EvaluateException
{
257 value
= evaluator
.evaluate(evaluationContext
);
259 setValueRunnable
.setValue(evaluationContext
, value
);
261 catch (IllegalArgumentException ex
) {
262 throw EvaluateExceptionUtil
.createEvaluateException(ex
.getMessage());
264 catch (InvalidTypeException ex
) {
265 throw EvaluateExceptionUtil
.createEvaluateException(DebuggerBundle
.message("evaluation.error.type.mismatch"));
267 catch (IncompatibleThreadStateException e
) {
268 throw EvaluateExceptionUtil
.createEvaluateException(e
);
270 catch (ClassNotLoadedException ex
) {
271 if (!evaluationContext
.isAutoLoadClasses()) {
272 throw EvaluateExceptionUtil
.createEvaluateException(ex
);
274 final ReferenceType refType
;
276 refType
= setValueRunnable
.loadClass(evaluationContext
, ex
.className());
277 if (refType
!= null) {
279 setValue(expressionToShow
, evaluator
, evaluationContext
, setValueRunnable
);
282 catch (InvocationException e
) {
283 throw EvaluateExceptionUtil
.createEvaluateException(e
);
285 catch (ClassNotLoadedException e
) {
286 throw EvaluateExceptionUtil
.createEvaluateException(e
);
288 catch (IncompatibleThreadStateException e
) {
289 throw EvaluateExceptionUtil
.createEvaluateException(e
);
291 catch (InvalidTypeException e
) {
292 throw EvaluateExceptionUtil
.createEvaluateException(e
);
294 catch (ObjectCollectedException e
) {
295 throw EvaluateExceptionUtil
.OBJECT_WAS_COLLECTED
;
300 private void askAndSet(final DebuggerTreeNodeImpl node
, final DebuggerContextImpl debuggerContext
, final SetValueRunnable setValueRunnable
) {
301 ProgressWindowWithNotification progressWindow
= new ProgressWindowWithNotification(true, debuggerContext
.getProject());
303 SuspendContextCommandImpl askSetAction
= new DebuggerContextCommandImpl(debuggerContext
) {
304 public Priority
getPriority() {
305 return Priority
.HIGH
;
308 public void threadAction() {
309 final NodeDescriptorImpl descriptor
= node
.getDescriptor();
310 String initialString
= "";
311 if (descriptor
instanceof ValueDescriptorImpl
) {
312 Value currentValue
= ((ValueDescriptorImpl
) descriptor
).getValue();
313 if (currentValue
instanceof StringReference
) {
314 initialString
= DebuggerUtilsEx
.getValueOrErrorAsString(debuggerContext
.createEvaluationContext(), currentValue
);
315 initialString
= initialString
== null ?
"" : "\"" + DebuggerUtilsEx
.translateStringValue(initialString
) + "\"";
317 else if (currentValue
instanceof PrimitiveValue
) {
318 ValueLabelRenderer renderer
= ((ValueDescriptorImpl
) descriptor
).getRenderer(debuggerContext
.getDebugProcess());
319 initialString
= getDisplayableString((PrimitiveValue
) currentValue
, renderer
instanceof NodeRenderer
&& HexRenderer
.UNIQUE_ID
.equals(renderer
.getUniqueId()));
322 final String initialString1
= initialString
;
323 final Project project
= debuggerContext
.getProject();
324 DebuggerInvocationUtil
.swingInvokeLater(project
, new Runnable() {
326 showEditor(new TextWithImportsImpl(CodeFragmentKind
.EXPRESSION
, initialString1
), node
, debuggerContext
, setValueRunnable
);
333 progressWindow
.setTitle(DebuggerBundle
.message("title.evaluating"));
334 debuggerContext
.getDebugProcess().getManagerThread().startProgress(askSetAction
, progressWindow
);
337 private void showEditor(final TextWithImports initialString
,
338 final DebuggerTreeNodeImpl node
,
339 final DebuggerContextImpl debuggerContext
,
340 final SetValueRunnable setValueRunnable
) {
341 final JPanel editorPanel
= new JPanel();
342 editorPanel
.setLayout(new BoxLayout(editorPanel
, BoxLayout
.X_AXIS
));
343 SimpleColoredComponent label
= new SimpleColoredComponent();
344 label
.setIcon(node
.getIcon());
345 DebuggerTreeRenderer
.getDescriptorTitle(debuggerContext
, node
.getDescriptor()).appendToComponent(label
);
346 editorPanel
.add(label
);
348 final DebuggerExpressionComboBox comboBox
= new DebuggerExpressionComboBox(
349 debuggerContext
.getProject(),
350 PositionUtil
.getContextElement(debuggerContext
),
351 "setValue", DefaultCodeFragmentFactory
.getInstance());
352 comboBox
.setText(initialString
);
353 comboBox
.selectAll();
354 editorPanel
.add(comboBox
);
356 final DebuggerTreeInplaceEditor editor
= new DebuggerTreeInplaceEditor(node
) {
357 public JComponent
createInplaceEditorComponent() {
361 public JComponent
getPreferredFocusedComponent() {
365 public Editor
getEditor() {
366 return comboBox
.getEditor();
369 public JComponent
getEditorComponent() {
370 return comboBox
.getEditorComponent();
373 private void flushValue() {
374 Editor editor
= getEditor();
379 final TextWithImports text
= comboBox
.getText();
381 PsiFile psiFile
= PsiDocumentManager
.getInstance(debuggerContext
.getProject()).getPsiFile(editor
.getDocument());
383 final ProgressWindowWithNotification progressWindow
= new ProgressWindowWithNotification(true, getProject());
384 EditorEvaluationCommand evaluationCommand
= new EditorEvaluationCommand(getEditor(), psiFile
, debuggerContext
, progressWindow
) {
385 public void threadAction() {
389 catch(EvaluateException e
) {
390 progressWindow
.cancel();
392 catch(ProcessCanceledException e
) {
393 progressWindow
.cancel();
396 if (!progressWindow
.isCanceled()) {
397 DebuggerInvocationUtil
.swingInvokeLater(debuggerContext
.getProject(), new Runnable() {
399 comboBox
.addRecent(text
);
407 protected Object
evaluate(final EvaluationContextImpl evaluationContext
) throws EvaluateException
{
408 ExpressionEvaluator evaluator
= DebuggerInvocationUtil
.commitAndRunReadAction(evaluationContext
.getProject(), new com
.intellij
.debugger
.EvaluatingComputable
<ExpressionEvaluator
>() {
409 public ExpressionEvaluator
compute() throws EvaluateException
{
410 return EvaluatorBuilderImpl
.getInstance().build(text
, ContextUtil
.getContextElement(evaluationContext
), ContextUtil
.getSourcePosition(evaluationContext
));
414 SetValueAction
.setValue(text
.getText(), evaluator
, evaluationContext
, new SetValueRunnable() {
415 public void setValue(EvaluationContextImpl evaluationContext
, Value newValue
) throws ClassNotLoadedException
,
416 InvalidTypeException
,
418 IncompatibleThreadStateException
{
419 if(!progressWindow
.isCanceled()) {
420 setValueRunnable
.setValue(evaluationContext
, newValue
);
425 public ReferenceType
loadClass(EvaluationContextImpl evaluationContext
, String className
) throws InvocationException
,
426 ClassNotLoadedException
,
428 IncompatibleThreadStateException
,
429 InvalidTypeException
{
430 return setValueRunnable
.loadClass(evaluationContext
, className
);
438 progressWindow
.addListener(new ProgressIndicatorListenerAdapter() {
439 //should return whether to stop processing
440 public void stopped() {
441 if(!progressWindow
.isCanceled()) {
442 IJSwingUtilities
.invoke(new Runnable() {
453 progressWindow
.setTitle(DebuggerBundle
.message("progress.set.value"));
454 debuggerContext
.getDebugProcess().getManagerThread().startProgress(evaluationCommand
, progressWindow
);
457 public void cancelEditing() {
459 super.cancelEditing();
466 public void doOKAction() {
477 final DebuggerStateManager stateManager
= DebuggerManagerEx
.getInstanceEx(debuggerContext
.getProject()).getContextManager();
479 stateManager
.addListener(new DebuggerContextListener() {
480 public void changeEvent(DebuggerContextImpl newContext
, int event
) {
481 stateManager
.removeListener(this);
482 editor
.cancelEditing();
486 node
.getTree().hideTooltip();
491 @SuppressWarnings({"HardCodedStringLiteral"})
492 private static String
getDisplayableString(PrimitiveValue value
, boolean showAsHex
) {
493 if (value
instanceof CharValue
) {
494 long longValue
= value
.longValue();
495 return showAsHex ?
"0x" + Long
.toHexString(longValue
).toUpperCase() : Long
.toString(longValue
);
497 if (value
instanceof ByteValue
) {
498 byte val
= value
.byteValue();
499 String strValue
= Integer
.toHexString(val
).toUpperCase();
500 if (strValue
.length() > 2) {
501 strValue
= strValue
.substring(strValue
.length() - 2);
503 return showAsHex ?
"0x" + strValue
: value
.toString();
505 if (value
instanceof ShortValue
) {
506 short val
= value
.shortValue();
507 String strValue
= Integer
.toHexString(val
).toUpperCase();
508 if (strValue
.length() > 4) {
509 strValue
= strValue
.substring(strValue
.length() - 4);
511 return showAsHex ?
"0x" + strValue
: value
.toString();
513 if (value
instanceof IntegerValue
) {
514 int val
= value
.intValue();
515 return showAsHex ?
"0x" + Integer
.toHexString(val
).toUpperCase() : value
.toString();
517 if (value
instanceof LongValue
) {
518 long val
= value
.longValue();
519 return showAsHex ?
"0x" + Long
.toHexString(val
).toUpperCase() + "L" : value
.toString() + "L";
521 return DebuggerUtilsEx
.translateStringValue(value
.toString());