| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.siyeh.ipp.unicode; |
| |
| import com.intellij.openapi.editor.CaretModel; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.SelectionModel; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.PsiElement; |
| import com.siyeh.ipp.base.Intention; |
| import com.siyeh.ipp.base.PsiElementEditorPredicate; |
| import com.siyeh.ipp.base.PsiElementPredicate; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * @author Bas Leijdekkers |
| */ |
| public class UnicodeUnescapeIntention extends Intention { |
| |
| @Override |
| protected void processIntention(@NotNull PsiElement element) {} |
| |
| @Override |
| protected void processIntention(Editor editor, @NotNull PsiElement element) { |
| final SelectionModel selectionModel = editor.getSelectionModel(); |
| if (selectionModel.hasSelection()) { |
| // does not check if octal escape is inside char or string literal (garbage in, garbage out) |
| final Document document = editor.getDocument(); |
| final int start = selectionModel.getSelectionStart(); |
| final int end = selectionModel.getSelectionEnd(); |
| final String text = document.getText(new TextRange(start, end)); |
| final int textLength = end - start; |
| final StringBuilder replacement = new StringBuilder(textLength); |
| int anchor = 0; |
| while (true) { |
| final int index = indexOfUnicodeEscape(text, anchor + 1); |
| if (index < 0) { |
| break; |
| } |
| replacement.append(text.substring(anchor, index)); |
| int hexStart = index + 1; |
| while (text.charAt(hexStart) == 'u') { |
| hexStart++; |
| } |
| anchor = hexStart + 4; |
| final int c = Integer.parseInt(text.substring(hexStart, anchor), 16); |
| replacement.appendCodePoint(c); |
| } |
| replacement.append(text.substring(anchor, textLength)); |
| document.replaceString(start, end, replacement); |
| } |
| else { |
| final CaretModel caretModel = editor.getCaretModel(); |
| final Document document = editor.getDocument(); |
| final int lineNumber = document.getLineNumber(caretModel.getOffset()); |
| final int lineStartOffset = document.getLineStartOffset(lineNumber); |
| final String line = document.getText(new TextRange(lineStartOffset, document.getLineEndOffset(lineNumber))); |
| final int column = caretModel.getLogicalPosition().column; |
| final int index1 = indexOfUnicodeEscape(line, column); |
| final int index2 = indexOfUnicodeEscape(line, column + 1); |
| final int escapeStart = index2 == column ? index2 : index1; // if caret is between two unicode escape, replace the right one |
| int hexStart = escapeStart + 1; |
| while (line.charAt(hexStart) == 'u') { |
| hexStart++; |
| } |
| final int c = Integer.parseInt(line.substring(hexStart, hexStart + 4), 16); |
| document.replaceString(lineStartOffset + escapeStart, lineStartOffset + hexStart + 4, String.valueOf((char) c)); |
| } |
| } |
| |
| /** |
| * see JLS 3.3. Unicode Escapes |
| */ |
| private static int indexOfUnicodeEscape(@NotNull String text, int offset) { |
| final int length = text.length(); |
| for (int i = 0; i < length; i++) { |
| final char c = text.charAt(i); |
| if (c != '\\') { |
| continue; |
| } |
| boolean isEscape = true; |
| int previousChar = i - 1; |
| while (previousChar >= 0 && text.charAt(previousChar) == '\\') { |
| isEscape = !isEscape; |
| previousChar--; |
| } |
| if (!isEscape) { |
| continue; |
| } |
| int nextChar = i; |
| do { |
| nextChar++; |
| if (nextChar >= length) { |
| break; |
| } |
| } |
| while (text.charAt(nextChar) == 'u'); // \uuuu0061 is a legal unicode escape |
| if (nextChar == i + 1 || nextChar + 3 >= length) { |
| break; |
| } |
| if (StringUtil.isHexDigit(text.charAt(nextChar)) && |
| StringUtil.isHexDigit(text.charAt(nextChar + 1)) && |
| StringUtil.isHexDigit(text.charAt(nextChar + 2)) && |
| StringUtil.isHexDigit(text.charAt(nextChar + 3))) { |
| final int escapeEnd = nextChar + 4; |
| if (offset <= escapeEnd) { |
| return i; |
| } |
| } |
| } |
| return -1; |
| } |
| |
| @NotNull |
| @Override |
| protected PsiElementPredicate getElementPredicate() { |
| return new UnicodeEscapePredicate(); |
| } |
| |
| private static class UnicodeEscapePredicate extends PsiElementEditorPredicate { |
| @Override |
| public boolean satisfiedBy(PsiElement element, @Nullable Editor editor) { |
| if (editor == null) { |
| return false; |
| } |
| final SelectionModel selectionModel = editor.getSelectionModel(); |
| final Document document = editor.getDocument(); |
| if (selectionModel.hasSelection()) { |
| final int start = selectionModel.getSelectionStart(); |
| final int end = selectionModel.getSelectionEnd(); |
| if (start < 0 || end < 0 || start > end) { |
| // shouldn't happen but http://ea.jetbrains.com/browser/ea_problems/50192 |
| return false; |
| } |
| final String text = document.getCharsSequence().subSequence(start, end).toString(); |
| return indexOfUnicodeEscape(text, 1) >= 0; |
| } |
| else { |
| final CaretModel caretModel = editor.getCaretModel(); |
| final int lineNumber = document.getLineNumber(caretModel.getOffset()); |
| final String line = document.getText(new TextRange(document.getLineStartOffset(lineNumber), document.getLineEndOffset(lineNumber))); |
| final int column = caretModel.getLogicalPosition().column; |
| final int index = indexOfUnicodeEscape(line, column); |
| return index >= 0 && column >= index; |
| } |
| } |
| } |
| } |