1 /*******************************************************************************
2  * Copyright (c) 2004, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.debug.ui.actions;
15 
16 import org.eclipse.debug.core.DebugException;
17 import org.eclipse.debug.core.model.IVariable;
18 import org.eclipse.debug.ui.actions.IVariableValueEditor;
19 import org.eclipse.jdt.internal.debug.eval.ast.engine.ASTInstructionCompiler;
20 import org.eclipse.jdt.internal.debug.ui.IJavaDebugHelpContextIds;
21 import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
22 import org.eclipse.jface.dialogs.IInputValidator;
23 import org.eclipse.jface.dialogs.InputDialog;
24 import org.eclipse.jface.window.Window;
25 import org.eclipse.osgi.util.NLS;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Control;
28 import org.eclipse.swt.widgets.Shell;
29 import org.eclipse.ui.IWorkbench;
30 import org.eclipse.ui.PlatformUI;
31 
32 /**
33  * A variable value editor that prompts the user to set a primitive's value.
34  */
35 public class JavaPrimitiveValueEditor implements IVariableValueEditor {
36 
37     /**
38      * The signature of the edited variable.
39      */
40     private String fSignature= null;
41 
42     /**
43      * Creates a new editor for a variable with the given signature
44      * @param signature the signature of the primitive to be edited
45      */
JavaPrimitiveValueEditor(String signature)46     public JavaPrimitiveValueEditor(String signature) {
47         fSignature= signature;
48     }
49 
50     /* (non-Javadoc)
51      * @see org.eclipse.debug.ui.actions.IVariableValueEditor#editVariable(org.eclipse.debug.core.model.IVariable, org.eclipse.swt.widgets.Shell)
52      */
53     @Override
editVariable(IVariable variable, Shell shell)54 	public boolean editVariable(IVariable variable, Shell shell) {
55         try {
56             String name= variable.getName();
57             String title= ActionMessages.JavaPrimitiveValueEditor_0;
58             String message= NLS.bind(ActionMessages.JavaPrimitiveValueEditor_1, new String[] {name});
59             String initialValue= variable.getValue().getValueString();
60             PrimitiveValidator validator= new PrimitiveValidator();
61             InputDialog dialog= new InputDialog(shell, title, message, initialValue, validator){
62             	@Override
63 				protected Control createDialogArea(Composite parent) {
64             		IWorkbench workbench = PlatformUI.getWorkbench();
65             		workbench.getHelpSystem().setHelp(parent, IJavaDebugHelpContextIds.DEFAULT_INPUT_DIALOG);
66             		return super.createDialogArea(parent);
67             	}
68             };
69             if (dialog.open() == Window.OK) {
70                 String stringValue = dialog.getValue();
71                 stringValue = formatValue(stringValue);
72                 if (stringValue.length() > 1 && stringValue.charAt(0) == '\\') {
73                 	// Compute value of octal of hexadecimal escape sequence
74                 	int i= validator.getEscapeValue(stringValue);
75                 	if (i != Integer.MAX_VALUE) {
76                 		stringValue= new String(new char[] { (char) i });
77                 	}
78                 }
79                 variable.setValue(stringValue);
80             }
81         } catch (DebugException e) {
82             JDIDebugUIPlugin.errorDialog(shell, ActionMessages.JavaPrimitiveValueEditor_2, ActionMessages.JavaPrimitiveValueEditor_3, e); //
83         }
84         return true;
85     }
86 
87     /* (non-Javadoc)
88      * @see org.eclipse.debug.ui.actions.IVariableValueEditor#saveVariable(org.eclipse.debug.core.model.IVariable, java.lang.String, org.eclipse.swt.widgets.Shell)
89      */
90     @Override
saveVariable(IVariable variable, String expression, Shell shell)91 	public boolean saveVariable(IVariable variable, String expression, Shell shell) {
92         return false;
93     }
94 
formatValue(String value)95     String formatValue(String value) {
96     	try {
97 	    	switch (fSignature.charAt(0)) {
98 		    	case 'I':
99 	    	    	return Integer.toString(ASTInstructionCompiler.parseIntValue(value));
100 		    	case 'J':
101 	    	    	return Long.toString(ASTInstructionCompiler.parseLongValue(value));
102 		    	case 'S':
103 	                return Short.toString(ASTInstructionCompiler.parseShortValue(value));
104 		    	case 'F':
105 		    	case 'D':
106 		    		return ASTInstructionCompiler.removePrefixZerosAndUnderscores(value, false);
107 		    	case 'B':
108 		    		return Byte.toString(ASTInstructionCompiler.parseByteValue(value));
109 		    }
110     	}
111     	catch(NumberFormatException nfe) {}
112     	return value;
113     }
114 
115     /**
116      * Input validator for primitive types
117      */
118     protected class PrimitiveValidator implements IInputValidator {
119         /* (non-Javadoc)
120          * @see org.eclipse.jface.dialogs.IInputValidator#isValid(java.lang.String)
121          */
122         @Override
isValid(String newText)123 		public String isValid(String newText) {
124             String type= null;
125             switch (fSignature.charAt(0)) {
126 	        	case 'B':
127 	        	    try {
128 		        	    Byte.parseByte(newText);
129 	                } catch (NumberFormatException e) {
130 	                    type= "byte"; //$NON-NLS-1$
131 	                }
132 	                break;
133 	        	case 'C':
134 	        		if (newText.length() > 1 && newText.charAt(0) == '\\') {
135 	        			// Possibly an escaped character
136 	        			if (isSpecialCharacter(newText) ||
137 	        					isOctalEscape(newText) ||
138 	        					isUnicode(newText)) {
139 	        				break;
140 	        			}
141 	        		}
142 	        		if (newText.length() != 1) {
143 	        	        type="char"; //$NON-NLS-1$
144 	        	    }
145 	                break;
146 	        	case 'D':
147 	        	    try {
148 	                    Double.parseDouble(ASTInstructionCompiler.removePrefixZerosAndUnderscores(newText, false));
149 	                } catch (NumberFormatException e) {
150 	        	        type="double"; //$NON-NLS-1$
151 	                }
152 	                break;
153 	        	case 'F':
154 	        	    try {
155 	                    Float.parseFloat(ASTInstructionCompiler.removePrefixZerosAndUnderscores(newText, false));
156 	                } catch (NumberFormatException e) {
157 	        	        type="float"; //$NON-NLS-1$
158 	                }
159 	                break;
160             	case 'I':
161             	    try {
162             	    	ASTInstructionCompiler.parseIntValue(newText);
163                     } catch (NumberFormatException e) {
164 	        	        type="int"; //$NON-NLS-1$
165                     }
166                     break;
167 	        	case 'J':
168 	        	    try {
169 	        	    	ASTInstructionCompiler.parseLongValue(newText);
170 	                } catch (NumberFormatException e) {
171 	        	        type="long"; //$NON-NLS-1$
172 	                }
173 	                break;
174 	        	case 'S':
175 	        	    try {
176 	                    ASTInstructionCompiler.parseShortValue(newText);
177 	                } catch (NumberFormatException e) {
178 	        	        type="short"; //$NON-NLS-1$
179 	                }
180 	                break;
181 	        	case 'Z':
182                     if (!("true".equals(newText) || "false".equals(newText))) { //$NON-NLS-1$ //$NON-NLS-2$
183 						type="boolean"; //$NON-NLS-1$
184                     }
185 	                break;
186             }
187             if (type != null) {
188                 return NLS.bind(ActionMessages.JavaPrimitiveValueEditor_4, new String[] { type });
189             }
190             return null;
191         }
192 
isUnicode(String newText)193 		private boolean isUnicode(String newText) {
194 			if (newText.length() == 6) {
195 				if (newText.charAt(1) == 'u') {
196 					char[] chars = newText.toCharArray();
197 					for (int i = 2; i < chars.length; i++) {
198 						if (!isHexDigit(chars[i])) {
199 							return false;
200 						}
201 					}
202 					return true;
203 				}
204 			}
205 			return false;
206 		}
207 
isOctalEscape(String newText)208 		private boolean isOctalEscape(String newText) {
209 			char[] chars= newText.toCharArray();
210 			if (chars.length < 4) {
211 				for (int i = 1; i < chars.length; i++) {
212 					if (!isOctalDigit(chars[i])) {
213 						return false;
214 					}
215 				}
216 				return true;
217 			} else if (chars.length == 4) {
218 				char ch= chars[1];
219 				if (ch < '0' || ch > '3') {
220 					return false;
221 				}
222 				for (int i = 2; i < chars.length; i++) {
223 					if (!isOctalDigit(chars[i])) {
224 						return false;
225 					}
226 				}
227                 return true;
228 			}
229 			return false;
230 		}
231 
isSpecialCharacter(String newText)232 		private boolean isSpecialCharacter(String newText) {
233 			char ch= newText.charAt(1);
234 			return newText.length() == 2 &&
235 				(ch == 'b'  ||
236 				ch == 't'  ||
237 				ch == 'n'  ||
238 				ch == 'f'  ||
239 				ch == 'r'  ||
240 				ch == '"'  ||
241 				ch == '\'' ||
242 				ch == '\\');
243 		}
244 
245 
isOctalDigit(char ch)246 		private boolean isOctalDigit(char ch) {
247             return Character.digit(ch, 8) != -1;
248 		}
249 
isHexDigit(char ch)250 		private boolean isHexDigit(char ch) {
251             return Character.digit(ch, 16) != -1;
252 		}
253 
254 		/**
255 		 * Returns the integer value specified by the given string, which
256 		 * represents an octal or hexadecimal escape sequence. Returns
257 		 * Integer.MAX_VALUE if the given string is not a valid octal or
258 		 * hexadecimal escape sequence.
259 		 *
260 		 * @param string
261 		 * @return
262 		 */
getEscapeValue(String string)263 		protected int getEscapeValue(String string) {
264 			return ASTInstructionCompiler.parseIntValue(string);
265 		}
266     }
267 
268 }
269