├── .gitignore ├── META-INF └── plugin.xml ├── README.md ├── codic-intellij-plugin.iml ├── codic-intellij-plugin.jar ├── img ├── screenshot1.png ├── screenshot2.png └── screenshot3.png └── src └── jp └── codic └── plugins └── intellij ├── CodicPlugin.java ├── CodicPluginApplicationComponent.java ├── CodicPluginConfigurable.java ├── CodicPluginProjectComponent.java ├── CodicPluginSettings.java ├── PreferenceForm.form ├── PreferenceForm.java ├── QuickLookAction.java ├── QuickLookForm.form ├── QuickLookForm.java ├── api ├── APIException.java ├── Candidate.java ├── CedEntry.java ├── CodicAPI.java ├── Translation.java ├── UserProject.java └── Word.java ├── i18n.properties ├── i18n_ja.properties ├── json ├── JSONArray.java ├── JSONException.java ├── JSONObject.java ├── JSONStringer.java ├── JSONTokener.java ├── JSONType.java └── JSONValue.java └── util ├── Debouncer.java ├── DefaultKeyListener.java └── DefaultMouseListener.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out 3 | -------------------------------------------------------------------------------- /META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | jp.codic.plugins.intellij 3 | Codic Support 4 | 5 | 1.1.1 6 | codic 7 | 8 | codic plugin for the IntelliJ platform products
10 | This plugin automatically generates (translate Japanese to English) naming using codic API.
11 | ]]>
12 | 13 | 1.1.1
15 | - Fixed bug #14.
16 | 17 | 1.1.0
18 | - Changed plugin name on JetBrains Plugins Repository.
19 | - Supported 2020.1.
20 | - Changed preference category into "Tools".
21 | 22 | 1.0.12
23 | - Fixed some bugs.
24 | - Separated brunches to support older version platforms.
25 | 26 | 1.0.11
27 | - Fixed bug #9 : Support 2016.1.
28 | 29 | 1.0.10
30 | - Fixed bug (Rate limit error not notified).
31 | 32 | 1.0.7
33 | - Added shortcut key for letter casing combo-box (Ctrl+Shift+D).
34 | 35 | 1.0.6
36 | - Support v1.1 API.
37 | - Enhance key handing in quick look.
38 | 39 | 1.0.5
40 | - Fix bug #6 : Add vertical scrollbar in quick-look.
41 | 42 | 1.0.4
43 | - Redesign the quick-look popup.
44 | - Fix bug #1 : IME dose not work in the quick-look.
45 | 46 | 1.0.3
47 | - Fix bug #2.
48 | 49 | 1.0.2
50 | - Display API error.
51 | 52 | 1.0.1
53 | - Support derived platform products.
54 | 55 | 1.0
56 | - First release. 57 | ]]> 58 |
59 | 60 | 61 | 62 | 63 | 65 | com.intellij.modules.lang 66 | 67 | 68 | 69 | 73 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | jp.codic.plugins.intellij.CodicPluginApplicationComponent 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | jp.codic.plugins.intellij.CodicPluginProjectComponent 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Codic Intellij IDEA Plugin 2 | IntelliJ IDEAとそのプラットフォーム製品 (PhpStorm, PyCharm, RubyMine, WebStorm, ...)、[Codic](https://codic.jp) プラグインです。 3 | 4 | ![codic plugin](https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/master/img/screenshot1.png) 5 | 6 | ### Install 7 | 1. インストールは公式レポジトリからインストールできます。 8 | メニューの "Preferences" >> "Plugins" から "Codic Support" で検索してインストールしてください。 9 | 10 | ![codic plugin](https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/master/img/screenshot2.png) 11 | 12 | 2. メニューの "Preferences" >> "Codic Support" でアクセストークンを設定します。アクセストークンは、 13 | [Codic](https://codic.jp)にログイン後、APIステータスのページより取得できます。 14 | 15 | ![codic plugin](https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/master/img/screenshot3.png) 16 | 17 | ### How to use 18 | 19 | エディタ上で、Ctrl+Shift+D でネーミング生成するためのポップアップを開きます。 20 | テキストを選択状態で開くと、ダイレクト生成できます。 21 | 22 | ※ このプラグインは、codic APIを使っているため、一定時間内のアクセス回数に制限があります。 23 | 制限を超えたら、APIステータスのページでリセットしてください。 24 | 25 | ### Change log 26 | 27 | _1.1.0_ 28 | - JetBrains Plugins Repository上でのプラグイン名を変更 29 | - 2020.1に対応 (#13) 30 | - PreferenceカテゴリーをToolsに変更 31 | 32 | _1.0.12_ 33 | - いくつかのバグを修正 34 | - 下位互換性の為に2020以降とブランチを分離 35 | 36 | _1.0.11_ 37 | - IntelliJ 2016.1 で設定ダイアログが表示されない問題を修正 38 | 39 | _1.0.10_ 40 | - APIレートリミットエラーを通知するように修正 41 | 42 | _1.0.7_ 43 | - ケース変換選択のショートカットを追加(Ctrl+Shift+Dをリタイプ) 44 | 45 | _1.0.6_ 46 | - API v1.1 をサポート. 47 | - ダイアログ上でのキーハンドリングを強化 48 | 49 | 57 | 58 | 59 | ### Bug report 60 | 61 | バグ・要望などがありましたら、[Issue](https://github.com/codic-project/codic-intellij-plugin/issues)へ登録してください。 62 | -------------------------------------------------------------------------------- /codic-intellij-plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /codic-intellij-plugin.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/dd86e0c50992bb462a63337117f0e534e696379c/codic-intellij-plugin.jar -------------------------------------------------------------------------------- /img/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/dd86e0c50992bb462a63337117f0e534e696379c/img/screenshot1.png -------------------------------------------------------------------------------- /img/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/dd86e0c50992bb462a63337117f0e534e696379c/img/screenshot2.png -------------------------------------------------------------------------------- /img/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codic-project/codic-intellij-plugin/dd86e0c50992bb462a63337117f0e534e696379c/img/screenshot3.png -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/CodicPlugin.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.ide.plugins.PluginManager; 4 | import com.intellij.openapi.extensions.PluginId; 5 | 6 | import java.text.MessageFormat; 7 | import java.util.ResourceBundle; 8 | 9 | 10 | public class CodicPlugin { 11 | 12 | private static final String PLUGIN_ID = "jp.codic.plugins.intellij"; 13 | 14 | private static ResourceBundle resource; 15 | 16 | public static String getString(String key) { 17 | if (resource == null) { 18 | resource = ResourceBundle.getBundle("jp/codic/plugins/intellij/i18n"); 19 | } 20 | return resource.getString(key); 21 | } 22 | 23 | public static String getString(String key, Object... args) { 24 | String message = getString(key); 25 | MessageFormat format = new MessageFormat(message); 26 | return format.format(args); 27 | } 28 | 29 | public static String getVersion() { 30 | return PluginManager.getPlugin(PluginId.getId(PLUGIN_ID)).getVersion(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/CodicPluginApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.openapi.components.ApplicationComponent; 4 | import com.intellij.openapi.components.ServiceManager; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | 8 | public class CodicPluginApplicationComponent implements ApplicationComponent { 9 | 10 | public static CodicPluginApplicationComponent getInstance() { 11 | return ServiceManager.getService(CodicPluginApplicationComponent.class); 12 | } 13 | 14 | public CodicPluginApplicationComponent() { 15 | } 16 | 17 | @NotNull 18 | public String getComponentName() { 19 | return "CodicPluginApplicationComponent"; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/CodicPluginConfigurable.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.openapi.components.*; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.openapi.options.BaseConfigurable; 6 | import com.intellij.openapi.options.Configurable; 7 | import com.intellij.openapi.options.ConfigurationException; 8 | import com.intellij.openapi.project.Project; 9 | import org.jetbrains.annotations.Nls; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | 15 | 16 | 17 | public class CodicPluginConfigurable extends BaseConfigurable { 18 | 19 | private final Logger LOG = Logger.getInstance("#" + getClass().getCanonicalName()); 20 | 21 | private Project project; 22 | private PreferenceForm gui; 23 | 24 | 25 | public CodicPluginConfigurable(Project project) { 26 | this.project =project; 27 | } 28 | 29 | @Nls 30 | @Override 31 | public String getDisplayName() { 32 | return "Codic Plugin"; 33 | } 34 | 35 | @Nullable 36 | public Icon getIcon() { 37 | return null; 38 | } 39 | 40 | @Nullable 41 | @Override 42 | public String getHelpTopic() { 43 | return null; 44 | } 45 | 46 | @Nullable 47 | @Override 48 | public JComponent createComponent() { 49 | if (gui == null) { 50 | gui = new PreferenceForm(getSettings()); 51 | } 52 | return gui.getRoot(); 53 | } 54 | 55 | @Override 56 | public boolean isModified() { 57 | return gui.isModified(getSettings()); 58 | } 59 | 60 | @Override 61 | public void apply() throws ConfigurationException { 62 | CodicPluginSettings settings = getSettings(); 63 | CodicPluginSettings newSettings = gui.exportDisplayedSettings(); 64 | // Copy properties. 65 | settings.setQuickLookHeight(newSettings.getQuickLookHeight()); 66 | settings.setQuickLookWidth(newSettings.getQuickLookWidth()); 67 | settings.setAccessToken(newSettings.getAccessToken()); 68 | settings.setProjectId(newSettings.getProjectId()); 69 | settings.setLetterCaseConvention(newSettings.getLetterCaseConvention()); 70 | 71 | } 72 | 73 | @Override 74 | public void reset() { 75 | gui.importFrom(getSettings()); 76 | } 77 | 78 | @Override 79 | public void disposeUIResources() { 80 | gui = null; 81 | } 82 | 83 | private CodicPluginSettings getSettings() { 84 | return CodicPluginProjectComponent.getInstance(project).getState(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/CodicPluginProjectComponent.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.util.xmlb.XmlSerializerUtil; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | 12 | @State(name = "CodicPluginSettings", storages = { 13 | @Storage("CodicPluginSettings.xml") 14 | }) 15 | public class CodicPluginProjectComponent implements 16 | PersistentStateComponent { 17 | 18 | private final Logger LOG = Logger.getInstance("#" + getClass().getCanonicalName()); 19 | 20 | 21 | private CodicPluginSettings settings; 22 | 23 | 24 | public CodicPluginProjectComponent(Project project) { 25 | } 26 | 27 | @NotNull 28 | public String getComponentName() { 29 | return "CodicPluginProjectComponent"; 30 | } 31 | 32 | @NotNull 33 | @Override 34 | public CodicPluginSettings getState() { 35 | if (settings == null) { 36 | settings = new CodicPluginSettings(); 37 | } 38 | return settings; 39 | } 40 | 41 | @Override 42 | public void loadState(CodicPluginSettings settings) { 43 | XmlSerializerUtil.copyBean(settings, this.getState()); 44 | } 45 | 46 | public static CodicPluginProjectComponent getInstance(Project project) { 47 | return project.getComponent(CodicPluginProjectComponent.class); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/CodicPluginSettings.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | 5 | import java.awt.*; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class CodicPluginSettings { 10 | private final Logger LOG = Logger.getInstance(CodicPluginSettings.class ); 11 | 12 | private String accessToken = ""; 13 | private Long projectId; 14 | private String letterCaseConvention = ""; 15 | private Integer quickLookHeight = 150; 16 | private Integer quickLookWidth = 300; 17 | private Map letterCaseConventionIndex = null; 18 | 19 | public CodicPluginSettings() { 20 | 21 | } 22 | 23 | public String getLetterCaseConvention() { 24 | return letterCaseConvention; 25 | } 26 | 27 | public void setLetterCaseConvention(String letterCaseConvention) { 28 | this.letterCaseConvention = letterCaseConvention; 29 | updateCaseConventionIndex(); 30 | } 31 | 32 | public void addLetterCaseConvention(String name, String id) { 33 | updateCaseConventionIndex(); 34 | letterCaseConventionIndex.put(name, id); 35 | updateCaseConventionRaw(); 36 | } 37 | 38 | public String findLetterCaseConvention(String name) { 39 | updateCaseConventionIndex(); 40 | return letterCaseConventionIndex.get(name); 41 | } 42 | 43 | public String getAccessToken() { 44 | return accessToken; 45 | } 46 | 47 | public void setAccessToken(String accessToken) { 48 | this.accessToken = accessToken; 49 | } 50 | 51 | public Long getProjectId() { 52 | return projectId; 53 | } 54 | 55 | public void setProjectId(Long projectId) { 56 | this.projectId = projectId; 57 | } 58 | 59 | private void updateCaseConventionRaw() { 60 | StringBuilder buffer = new StringBuilder(); 61 | for (Map.Entry entry : letterCaseConventionIndex.entrySet()) { 62 | buffer.append(entry.getKey() + ":" + entry.getValue()).append(","); 63 | } 64 | this.letterCaseConvention = buffer.toString(); 65 | } 66 | 67 | private void updateCaseConventionIndex() { 68 | if (letterCaseConventionIndex == null) { 69 | letterCaseConventionIndex = new HashMap(); 70 | for (String text : letterCaseConvention.split(",")) { 71 | if (text.isEmpty() || text.indexOf(":") == -1) 72 | continue; 73 | String[] fields = text.split(":"); 74 | if (fields.length <= 1) 75 | continue; 76 | letterCaseConventionIndex.put(fields[0], fields[1]); 77 | } 78 | } 79 | } 80 | 81 | public Integer getQuickLookHeight() { 82 | return quickLookHeight; 83 | } 84 | 85 | public void setQuickLookHeight(Integer quickLookHeight) { 86 | this.quickLookHeight = quickLookHeight; 87 | } 88 | 89 | public Integer getQuickLookWidth() { 90 | return quickLookWidth; 91 | } 92 | 93 | public void setQuickLookWidth(Integer quickLookWidth) { 94 | this.quickLookWidth = quickLookWidth; 95 | } 96 | 97 | 98 | public void updateQuickLookSize(Dimension quickLookSize) { 99 | quickLookWidth = new Double(quickLookSize.getWidth()).intValue(); 100 | quickLookHeight = new Double(quickLookSize.getHeight()).intValue(); 101 | } 102 | 103 | public Dimension quickLookSize() { 104 | if (quickLookWidth == -1 || quickLookHeight == -1) 105 | return null; 106 | return new Dimension(quickLookWidth, quickLookHeight); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/PreferenceForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
73 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/PreferenceForm.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.ui.ListCellRendererWrapper; 4 | import jp.codic.plugins.intellij.api.APIException; 5 | import jp.codic.plugins.intellij.api.CodicAPI; 6 | import jp.codic.plugins.intellij.api.UserProject; 7 | 8 | import javax.swing.*; 9 | import java.awt.event.FocusEvent; 10 | import java.awt.event.FocusListener; 11 | 12 | public class PreferenceForm { 13 | 14 | private JPanel root; 15 | private DefaultComboBoxModel projectFieldModel; 16 | private JComboBox projectField; 17 | private JTextField accessTokenField; 18 | private JLabel accessTokenErrorLabel; 19 | private CodicPluginSettings settings; 20 | 21 | public PreferenceForm(CodicPluginSettings settings) { 22 | this.settings = settings; 23 | initModel(settings); 24 | } 25 | 26 | private void initModel(CodicPluginSettings settings) { 27 | 28 | projectFieldModel = new DefaultComboBoxModel(); 29 | projectField.setModel(projectFieldModel); 30 | projectField.setRenderer(new ListCellRendererWrapper() { 31 | @Override 32 | public void customize(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 33 | if (value instanceof UserProject) 34 | setText(((UserProject) value).name); 35 | } 36 | }); 37 | 38 | accessTokenField.addFocusListener(new FocusListener() { 39 | @Override 40 | public void focusGained(FocusEvent focusEvent) { 41 | // Pass 42 | } 43 | @Override 44 | public void focusLost(FocusEvent focusEvent) { 45 | updateUserProjects(); 46 | } 47 | }); 48 | } 49 | 50 | private void updateUserProjects() { 51 | updateUserProjects(null); 52 | } 53 | 54 | private void updateUserProjects(Long selected) { 55 | 56 | // Clear message. 57 | accessTokenErrorLabel.setText(null); 58 | projectField.setEnabled(false); 59 | projectFieldModel.removeAllElements(); 60 | 61 | try { 62 | 63 | // Update project options. 64 | String accessToken = accessTokenField.getText(); 65 | 66 | UserProject[] userProjects = new UserProject[0]; 67 | if (accessToken != null && !accessToken.equals("")) { 68 | userProjects = CodicAPI.getUserProjects(accessToken); 69 | } 70 | 71 | projectFieldModel.removeAllElements(); 72 | 73 | if (userProjects.length > 0) { 74 | UserProject selectedItem = userProjects[0]; 75 | for (UserProject userProject : userProjects) { 76 | projectFieldModel.addElement(userProject); 77 | if (userProject.id.equals(selected)) { 78 | selectedItem = userProject; 79 | } 80 | } 81 | projectFieldModel.setSelectedItem(selectedItem); 82 | projectField.setEnabled(true); 83 | } 84 | 85 | } catch (APIException e) { 86 | accessTokenErrorLabel.setText(e.getMessage()); 87 | } 88 | } 89 | 90 | public JPanel getRoot() { 91 | return root; 92 | } 93 | 94 | public void importFrom(CodicPluginSettings data) { 95 | initModel(data); 96 | accessTokenField.setText(settings.getAccessToken()); 97 | updateUserProjects(data.getProjectId()); 98 | } 99 | 100 | public CodicPluginSettings exportDisplayedSettings() { 101 | settings.setAccessToken(accessTokenField.getText()); 102 | UserProject userProject = (UserProject)projectFieldModel.getSelectedItem(); 103 | settings.setProjectId(userProject != null ? userProject.id : null); 104 | 105 | return settings; 106 | } 107 | 108 | public boolean isModified(CodicPluginSettings data) { 109 | if (isModifiedCustom(data)) { 110 | return true; 111 | } 112 | return false; 113 | } 114 | 115 | private boolean isModifiedCustom(CodicPluginSettings data) { 116 | if (projectFieldModel.getSelectedItem() != data.getProjectId()) { 117 | return true; 118 | } 119 | if (!this.accessTokenField.getText().equals(data.getAccessToken())) { 120 | return true; 121 | } 122 | 123 | return true; 124 | } 125 | 126 | 127 | private void createUIComponents() { 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/QuickLookAction.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.ide.IdeBundle; 4 | import com.intellij.notification.Notification; 5 | import com.intellij.notification.NotificationType; 6 | import com.intellij.notification.Notifications; 7 | import com.intellij.openapi.actionSystem.AnAction; 8 | import com.intellij.openapi.actionSystem.AnActionEvent; 9 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 10 | import com.intellij.openapi.application.ApplicationManager; 11 | import com.intellij.openapi.command.CommandProcessor; 12 | import com.intellij.openapi.editor.Editor; 13 | import com.intellij.openapi.editor.EditorModificationUtil; 14 | import com.intellij.openapi.editor.SelectionModel; 15 | import com.intellij.openapi.fileEditor.FileDocumentManager; 16 | import com.intellij.openapi.project.Project; 17 | import com.intellij.openapi.ui.popup.ComponentPopupBuilder; 18 | import com.intellij.openapi.ui.popup.JBPopup; 19 | import com.intellij.openapi.ui.popup.JBPopupListener; 20 | import com.intellij.openapi.ui.popup.LightweightWindowEvent; 21 | import com.intellij.openapi.vfs.VirtualFile; 22 | import com.intellij.ui.popup.PopupFactoryImpl; 23 | import jp.codic.plugins.intellij.api.APIException; 24 | 25 | import javax.swing.*; 26 | import java.awt.*; 27 | import java.awt.event.AWTEventListener; 28 | import java.awt.event.MouseEvent; 29 | 30 | public class QuickLookAction extends AnAction { 31 | 32 | private QuickLookForm form = null; 33 | private JBPopup popup; 34 | private CancelListener cancelListener; 35 | private CodicPluginProjectComponent component; 36 | 37 | public void actionPerformed(AnActionEvent e) { 38 | 39 | Editor editor = PlatformDataKeys.EDITOR_EVEN_IF_INACTIVE.getData(e.getDataContext()); 40 | if (editor == null) { 41 | return; 42 | } 43 | 44 | 45 | // TODO : リンクの入れ方 46 | // http://www.programcreek.com/java-api-examples/index.php?api=com.intellij.notification.Notifications 47 | // https://github.com/carymrobbins/intellij-haskforce/blob/master/src/com/haskforce/utils/HaskellToolsNotificationListener.java 48 | 49 | if (!isConfigured(editor.getProject())) { 50 | Notifications.Bus.notify( 51 | new Notification("CodicPlugin", "Codic Plugin", 52 | CodicPlugin.getString("messages.access_token_required"), NotificationType.WARNING) 53 | ); 54 | return; 55 | } 56 | 57 | VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); 58 | String fileName = virtualFile.getFileType().getName(); 59 | 60 | String text = getSelectedText(editor); 61 | 62 | component = editor.getProject().getComponent(CodicPluginProjectComponent.class); 63 | popup = showPopup(editor); 64 | form.beforeShow(fileName, text); 65 | } 66 | 67 | private boolean isConfigured(Project project) { 68 | CodicPluginProjectComponent component = project.getComponent(CodicPluginProjectComponent.class); 69 | CodicPluginSettings settings = component.getState(); 70 | 71 | return settings.getAccessToken() != null && 72 | !settings.getAccessToken().equals(""); 73 | } 74 | 75 | private JBPopup showPopup(final Editor editor) { 76 | final Project project = editor.getProject(); 77 | if (form == null) { 78 | form = new QuickLookForm(editor.getProject()); 79 | } 80 | 81 | form.setSelectionListener(new QuickLookForm.EventListener() { 82 | @Override 83 | public void selected(final String text) { 84 | CommandProcessor.getInstance().executeCommand(editor.getProject(), new Runnable() { 85 | public void run() { 86 | ApplicationManager.getApplication().runWriteAction(new Runnable() { 87 | public void run() { 88 | EditorModificationUtil.deleteSelectedText(editor); 89 | EditorModificationUtil.insertStringAtCaret(editor, text); 90 | popup.cancel(); 91 | popup = null; 92 | } 93 | }); 94 | } 95 | }, IdeBundle.message("command.pasting.reference"), null); 96 | } 97 | 98 | @Override 99 | public void failed(APIException e) { 100 | String message = e.getMessage(); 101 | if (e.getCode() == 429) { // Rate limit exceeded. 102 | message = CodicPlugin.getString("messages.api_rate_limit_exceeded"); 103 | } 104 | Notifications.Bus.notify( 105 | new Notification("CodicPlugin", "Codic Plugin", message, NotificationType.WARNING), 106 | project 107 | ); 108 | } 109 | }); 110 | 111 | PopupFactoryImpl popupFactory = new PopupFactoryImpl(); 112 | ComponentPopupBuilder builder = popupFactory.createComponentPopupBuilder( 113 | form.getRoot(), form.getPreferredControl()); 114 | builder.setResizable(true) 115 | .setRequestFocus(true) 116 | .setCancelOnClickOutside(false) // Bugfix : Cancel manually. 117 | .addListener(new JBPopupListener() { 118 | @Override 119 | public void beforeShown(LightweightWindowEvent lightweightWindowEvent) { 120 | 121 | } 122 | 123 | @Override 124 | public void onClosed(LightweightWindowEvent lightweightWindowEvent) { 125 | if (cancelListener != null) { 126 | Toolkit.getDefaultToolkit().removeAWTEventListener(cancelListener); 127 | cancelListener = null; 128 | } 129 | 130 | // Save latest size. 131 | component.getState().updateQuickLookSize(popup.getContent().getSize()); 132 | popup.dispose(); 133 | popup = null; 134 | } 135 | }); 136 | 137 | final JBPopup popup = builder.createPopup(); 138 | Dimension size = component.getState().quickLookSize(); 139 | if (size != null) { 140 | popup.setSize(size); 141 | } 142 | 143 | popup.showInBestPositionFor(editor); 144 | 145 | cancelListener = new CancelListener(popup) { 146 | @Override public void cancel() { 147 | popup.cancel(); 148 | } 149 | }; 150 | 151 | // Bugfix : Cancel manually. 152 | Toolkit.getDefaultToolkit().addAWTEventListener(this.cancelListener, AWTEvent.MOUSE_MOTION_EVENT_MASK 153 | | AWTEvent.MOUSE_EVENT_MASK 154 | | AWTEvent.KEY_EVENT_MASK); 155 | 156 | return popup; 157 | } 158 | 159 | private String getSelectedText(Editor editor) { 160 | SelectionModel selectedModel = editor.getSelectionModel(); 161 | return selectedModel.getSelectedText(); 162 | } 163 | 164 | 165 | public abstract class CancelListener implements AWTEventListener { 166 | 167 | JBPopup popup; 168 | public CancelListener(JBPopup popup) { 169 | this.popup = popup; 170 | } 171 | 172 | public abstract void cancel(); 173 | 174 | @Override 175 | public void eventDispatched(AWTEvent awtEvent) { 176 | if (awtEvent instanceof MouseEvent) { 177 | MouseEvent mouseEvent = (MouseEvent)awtEvent; 178 | if (mouseEvent.getID() == MouseEvent.MOUSE_CLICKED && !withinPopup(awtEvent)) { 179 | cancel(); 180 | } 181 | } 182 | } 183 | 184 | private boolean withinPopup(final AWTEvent event) { 185 | final MouseEvent mouse = (MouseEvent)event; 186 | final Point point = mouse.getPoint(); 187 | SwingUtilities.convertPointToScreen(point, mouse.getComponent()); 188 | return (new Rectangle(this.popup.getLocationOnScreen(), this.popup.getSize())).contains(point); 189 | } 190 | } 191 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/QuickLookForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/QuickLookForm.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij; 2 | 3 | import com.intellij.openapi.actionSystem.KeyboardShortcut; 4 | import com.intellij.openapi.actionSystem.Shortcut; 5 | import com.intellij.openapi.diagnostic.Logger; 6 | import com.intellij.openapi.keymap.KeymapManager; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.ui.ListCellRendererWrapper; 9 | import com.intellij.util.ui.UIUtil; 10 | import jp.codic.plugins.intellij.api.*; 11 | import jp.codic.plugins.intellij.util.Debouncer; 12 | import jp.codic.plugins.intellij.util.DefaultKeyListener; 13 | import jp.codic.plugins.intellij.util.DefaultMouseListener; 14 | 15 | import javax.swing.*; 16 | import javax.swing.border.EmptyBorder; 17 | import javax.swing.event.DocumentEvent; 18 | import javax.swing.event.DocumentListener; 19 | import java.awt.*; 20 | import java.awt.event.*; 21 | 22 | 23 | public class QuickLookForm { 24 | 25 | private static final long DELAY_FOR_KEY_EVENT = 200; 26 | private static final String QUICK_LOOK_ACTION_ID = "CodicPluginQuickLookAction"; 27 | private final Logger LOG = Logger.getInstance("#" + getClass().getCanonicalName()); 28 | 29 | private JPanel rootPanel; 30 | private JList candidatesList; 31 | private DefaultListModel candidatesListModel; 32 | private JComboBox letterCaseComboBox; 33 | private JTextField queryTextField; 34 | private JLabel statusLabel; 35 | private Debouncer debouncer ; 36 | private EventListener listener; 37 | private CodicPluginProjectComponent component; 38 | private String activeFileType; 39 | private KeyStroke actionShortcutKey; 40 | 41 | /** 42 | * A constructor. 43 | */ 44 | public QuickLookForm(Project project) { 45 | 46 | debouncer = new Debouncer(DELAY_FOR_KEY_EVENT); 47 | component = project.getComponent(CodicPluginProjectComponent.class); 48 | actionShortcutKey = getShortcut(); 49 | 50 | // Init query text field. 51 | queryTextField.getDocument().addDocumentListener(new ModifyListener() { 52 | @Override public void changed(DocumentEvent documentEvent) { 53 | updateSearch(); 54 | } 55 | }); 56 | 57 | queryTextField.addKeyListener(new DefaultKeyListener() { 58 | @Override public void keyPressed(KeyEvent keyEvent) { 59 | if (keyEvent.getKeyCode() == 38 || 60 | keyEvent.getKeyCode() == 40) { 61 | scrollCandidatesList(keyEvent.getKeyCode() == 38); 62 | keyEvent.consume(); // Prevent default behavior. 63 | } 64 | if (keyEvent.getKeyCode() == 10) { // Enter 65 | applySelection(); 66 | } 67 | } 68 | @Override public void keyReleased(KeyEvent keyEvent) { 69 | // Note: keyReleasedでないとハンドリングできない、ただしOracleのJDKはkeyPressedでもハンドリングできる 70 | // Note: KeyEvent#code, KeyEvent#modifiers は keyStroke と互換性がないので変換している 71 | KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(keyEvent); 72 | if (actionShortcutKey != null && (actionShortcutKey.getKeyCode() == keyStroke.getKeyCode() && 73 | actionShortcutKey.getModifiers() == keyStroke.getModifiers())) { 74 | keyEvent.consume(); 75 | scrollLetterCaseComboBox(); 76 | } 77 | } 78 | }); 79 | 80 | 81 | // Init letter case combo box. 82 | for (LetterCase letterCase : LetterCase.ENTRIES) { 83 | letterCaseComboBox.addItem(letterCase); 84 | } 85 | 86 | letterCaseComboBox.setRenderer(new ListCellRendererWrapper() { 87 | @Override public void customize(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 88 | if (value instanceof LetterCase) 89 | setText(((LetterCase) value).shortName); 90 | } 91 | }); 92 | letterCaseComboBox.addItemListener(new ItemListener() { 93 | @Override 94 | public void itemStateChanged(ItemEvent event) { 95 | if (event.getStateChange() == ItemEvent.SELECTED) { 96 | //Object item = event.getItem(); 97 | updateSearch(); 98 | component.getState().addLetterCaseConvention(activeFileType, 99 | ((LetterCase)letterCaseComboBox.getSelectedItem()).id); 100 | } 101 | } 102 | }); 103 | 104 | // Init result list. 105 | candidatesListModel = new DefaultListModel(); 106 | candidatesList.setModel(candidatesListModel); 107 | candidatesList.setBorder(new EmptyBorder(2, 4, 4, 2)); 108 | candidatesList.setFont(increaseFontSize(candidatesList.getFont(), +1)); 109 | candidatesList.addMouseListener(new DefaultMouseListener() { 110 | @Override public void mouseClicked(MouseEvent mouseEvent) { 111 | if (mouseEvent.getClickCount() >= 2) { 112 | applySelection(); 113 | } 114 | } 115 | }); 116 | candidatesList.addKeyListener(new DefaultKeyListener() { 117 | @Override public void keyPressed(KeyEvent keyEvent) { 118 | if (keyEvent.getKeyCode() == 10) { // Enter 119 | applySelection(); 120 | } 121 | } 122 | }); 123 | 124 | queryTextField.putClientProperty("JTextField.variant", "search"); 125 | 126 | // Broken char ... 127 | // statusLabel.setText("Press " + getKeyStrokeLabel( 128 | // getShortcut(QUICK_LOOK_ACTION_ID)) + " : Change letter case."); 129 | } 130 | 131 | private KeyStroke getShortcut() { 132 | final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(QUICK_LOOK_ACTION_ID); 133 | for (final Shortcut shortcut : shortcuts) { 134 | if (shortcut instanceof KeyboardShortcut) { 135 | return ((KeyboardShortcut)shortcut).getFirstKeyStroke(); 136 | } 137 | } 138 | return null; 139 | } 140 | 141 | private String getKeyStrokeLabel(KeyStroke myKeyStroke) { 142 | StringBuilder sb = new StringBuilder(); 143 | if ((myKeyStroke.getModifiers() & KeyEvent.CTRL_MASK) > 0) { 144 | sb.append("Ctrl + "); 145 | } 146 | if ((myKeyStroke.getModifiers() & KeyEvent.SHIFT_MASK) > 0) { 147 | sb.append("Shift + "); 148 | } 149 | if ((myKeyStroke.getModifiers() & KeyEvent.META_MASK) > 0) { 150 | sb.append("Meta + "); 151 | } 152 | sb.append(myKeyStroke.getKeyChar()); 153 | return sb.toString(); 154 | } 155 | 156 | public void setSelectionListener(EventListener listener) { 157 | this.listener = listener; 158 | } 159 | 160 | public void beforeShow(String fileType, String text) { 161 | this.queryTextField.setText(text); 162 | this.candidatesListModel.removeAllElements(); 163 | this.activeFileType = fileType; 164 | 165 | String letterCase = component.getState().findLetterCaseConvention(fileType); 166 | if (letterCase != null) { 167 | this.letterCaseComboBox.setSelectedItem(LetterCase.valueOf(letterCase)); 168 | } else { 169 | this.letterCaseComboBox.setSelectedIndex(0); 170 | } 171 | 172 | } 173 | 174 | public void applySelection() { 175 | String selected = this.getSelected(); 176 | if (selected != null && listener != null) { 177 | listener.selected(selected); 178 | } 179 | } 180 | 181 | public String getSelected() { 182 | String selected = (String)this.candidatesList.getSelectedValue(); 183 | if (selected == null) { 184 | return null; 185 | } 186 | return selected; 187 | } 188 | 189 | public JComponent getRoot() { 190 | return this.rootPanel; 191 | } 192 | 193 | public JComponent getPreferredControl() { 194 | return this.queryTextField; 195 | } 196 | 197 | // Private methods ------- 198 | 199 | private void updateSearch() { 200 | CodicPluginSettings settings = component.getState(); 201 | debouncer.push(new SearchTask(settings, queryTextField.getText(), 202 | ((LetterCase)letterCaseComboBox.getSelectedItem()).id)); 203 | } 204 | 205 | private void updateResultList(Translation[] translations) { 206 | UIUtil.invokeLaterIfNeeded( 207 | new CandidatesListUpdater(translations)); 208 | } 209 | 210 | private void setStatusLabelAsync(String message) { 211 | UIUtil.invokeLaterIfNeeded( 212 | new StatusUpdater(true, message)); 213 | } 214 | 215 | private void scrollCandidatesList(boolean reverse) { 216 | int index = this.candidatesList.getSelectedIndex(); 217 | int size = this.candidatesList.getModel().getSize(); 218 | if (size > 0) { 219 | index += (reverse ? -1 : 1); 220 | if (index < 0) { 221 | index = size - 1; 222 | } else if (index > size - 1) { 223 | index = 0; 224 | } 225 | System.out.println(">" + index); 226 | this.candidatesList.setSelectedIndex(index); 227 | } 228 | } 229 | 230 | private Font increaseFontSize(Font baseFont, int delta) { 231 | return new Font(baseFont.getName(), baseFont.getStyle(), baseFont.getSize() + delta); 232 | } 233 | 234 | private class SearchTask implements Runnable { 235 | private CodicPluginSettings settings; 236 | private String text; 237 | private String letterCase; 238 | 239 | private SearchTask(CodicPluginSettings settings, String text, String letterCase) { 240 | this.settings = settings; 241 | this.text = text; 242 | this.letterCase = letterCase; 243 | } 244 | 245 | @Override 246 | public void run() { 247 | if (this.text == null || this.text.equals("")) { 248 | updateResultList(new Translation[0]); 249 | return; 250 | } 251 | 252 | try { 253 | Translation[] translations = CodicAPI.translate(this.settings.getAccessToken(), 254 | this.settings.getProjectId(), this.text, this.letterCase); 255 | 256 | updateResultList(translations); 257 | 258 | } catch (APIException e) { 259 | updateResultList(new Translation[0]); 260 | listener.failed(e); 261 | } 262 | } 263 | } 264 | 265 | private void scrollLetterCaseComboBox() { 266 | int index = letterCaseComboBox.getSelectedIndex(); 267 | int size = letterCaseComboBox.getModel().getSize(); 268 | if (size > 0) { 269 | index++; 270 | if (index > size - 1) { 271 | index = 0; 272 | } 273 | letterCaseComboBox.setSelectedIndex(index); // Fire event selection change. 274 | } 275 | } 276 | 277 | 278 | private static class LetterCase { 279 | private String id; 280 | private String name; 281 | private String shortName; 282 | 283 | public static final LetterCase[] ENTRIES = { 284 | new LetterCase("pascal", "PascalCase", "Aa"), 285 | new LetterCase("camel", "camelCase", "aA"), 286 | new LetterCase("lower underscore", "snake_case (小文字)", "a_a"), 287 | new LetterCase("upper underscore", "SNAKE_CASE (大文字)", "A_A"), 288 | new LetterCase("hyphen", "ハイフネーション", "a-a"), 289 | new LetterCase("", "変換なし", "a a"), 290 | }; 291 | 292 | public static LetterCase valueOf(String id) { 293 | for (int i= 0; i < ENTRIES.length; i++) { 294 | if (id.equals(ENTRIES[i].id)) { 295 | return ENTRIES[i]; 296 | } 297 | } 298 | return null; 299 | } 300 | 301 | private LetterCase(String id, String name, String shortName) { 302 | this.id = id; 303 | this.name = name; 304 | this.shortName = shortName; 305 | } 306 | } 307 | 308 | 309 | abstract private class ModifyListener implements DocumentListener { 310 | @Override 311 | public void insertUpdate(DocumentEvent documentEvent) { 312 | changed(documentEvent); 313 | } 314 | @Override 315 | public void removeUpdate(DocumentEvent documentEvent) { 316 | changed(documentEvent); 317 | } 318 | @Override 319 | public void changedUpdate(DocumentEvent documentEvent) { 320 | changed(documentEvent); 321 | } 322 | abstract public void changed(DocumentEvent documentEvent); 323 | } 324 | 325 | 326 | private class StatusUpdater implements Runnable { 327 | private boolean status; 328 | private String message; 329 | 330 | private StatusUpdater(boolean status, String message) { 331 | this.status = status; 332 | this.message = message; 333 | } 334 | 335 | @Override 336 | public void run() { 337 | statusLabel.setText(message); 338 | } 339 | } 340 | 341 | private class CandidatesListUpdater implements Runnable { 342 | private Translation[] translations; 343 | 344 | private CandidatesListUpdater(Translation[] translations) { 345 | this.translations = translations; 346 | } 347 | 348 | @Override 349 | public void run() { 350 | candidatesListModel.removeAllElements(); 351 | if (translations.length > 0) { 352 | if (translations[0].words.length == 1 && translations[0].words[0].successful) { 353 | for (Candidate candidate : translations[0].words[0].candidates) { 354 | candidatesListModel.addElement(candidate.textInCasing); 355 | } 356 | } else { 357 | candidatesListModel.addElement(translations[0].translatedTextInCasing); 358 | } 359 | } 360 | } 361 | } 362 | 363 | public static interface EventListener { 364 | public void selected(String text); 365 | public void failed(APIException e); 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/APIException.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | /** 4 | * Exception that throws when API acccess faild. 5 | */ 6 | public class APIException extends Exception { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | private int code; 11 | 12 | public APIException(String message) { 13 | super(message); 14 | } 15 | 16 | public APIException(String message, int code) { 17 | super(message); 18 | this.code = code; 19 | } 20 | 21 | public int getCode() { 22 | return this.code; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/Candidate.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | public class Candidate { 4 | 5 | public String text; 6 | public String textInCasing; 7 | } 8 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/CedEntry.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | public class CedEntry { 4 | 5 | public String title; 6 | public String digest; 7 | } 8 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/CodicAPI.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.util.net.HttpConfigurable; 6 | import jp.codic.plugins.intellij.CodicPlugin; 7 | import jp.codic.plugins.intellij.json.JSONArray; 8 | import jp.codic.plugins.intellij.json.JSONException; 9 | import jp.codic.plugins.intellij.json.JSONObject; 10 | 11 | import java.io.*; 12 | import java.net.HttpURLConnection; 13 | import java.net.URL; 14 | import java.net.URLConnection; 15 | import java.net.URLEncoder; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | public class CodicAPI { 20 | 21 | private static final String HOST = "https://api.codic.jp"; 22 | private static final Logger LOG = Logger.getInstance(CodicAPI.class); 23 | 24 | /** 25 | * /v1/user_projects.json 26 | * @throws APIException 27 | */ 28 | public static UserProject[] getUserProjects(String accessToken) throws APIException { 29 | String url = HOST + "/v1/user_projects.json"; 30 | 31 | Map params = new HashMap(); 32 | try { 33 | 34 | JSONArray json = execHttpGetJsonArray(accessToken, url + "?" + buildQueryString(params)); 35 | UserProject[] entries = new UserProject[json.length()]; 36 | for (int i = 0; i < json.length(); i++) { 37 | entries[i] = new UserProject(); 38 | entries[i].id = json.getJSONObject(i).getLong("id"); 39 | entries[i].name = json.getJSONObject(i).getString("name"); 40 | } 41 | return entries; 42 | } catch (Exception e) { 43 | throw new APIException(e.getMessage()); 44 | } 45 | } 46 | 47 | /** 48 | * @param accessToken The access token. 49 | * @param projectId The project id. 50 | * @param query Text to translate. 51 | * @return Array of Translations 52 | * @throws APIException 53 | */ 54 | public static Translation[] translate(String accessToken, Long projectId, String query) 55 | throws APIException { 56 | return translate(accessToken, projectId, query, null); 57 | } 58 | 59 | /** 60 | * @param accessToken The access token. 61 | * @param projectId The project id. 62 | * @param query Text to translate. 63 | * @return Array of Translations 64 | * @throws APIException 65 | */ 66 | public static Translation[] translate(String accessToken, Long projectId, String query, String letterCase) 67 | throws APIException { 68 | String url = HOST + "/v1.1/engine/translate.json"; 69 | Map params = new HashMap(); 70 | params.put("project_id", projectId); 71 | params.put("text", query); 72 | params.put("casing", letterCase); 73 | 74 | try { 75 | JSONArray json = execHttpGetJsonArray(accessToken, url + "?" + buildQueryString(params)); 76 | Translation[] entries = new Translation[json.length()]; 77 | for (int i = 0; i < json.length(); i++) { 78 | entries[i] = new Translation(); 79 | entries[i].translatedText = json.getJSONObject(i).getString("translated_text"); 80 | entries[i].translatedTextInCasing = json.getJSONObject(i).getString("translated_text_in_casing"); 81 | entries[i].words = mapWords(json.getJSONObject(i).getJSONArray("words")); 82 | break; 83 | } 84 | return entries; 85 | } catch (APIException e) { 86 | throw e; 87 | } catch (Exception e) { 88 | throw new APIException(e.getMessage()); 89 | } 90 | } 91 | 92 | public static CedEntry[] lookup(String accessToken, String query) 93 | throws APIException { 94 | String url = HOST + "/v1/ced/lookup.json"; 95 | Map params = new HashMap(); 96 | params.put("query", query); 97 | params.put("count", 20); 98 | 99 | try { 100 | JSONArray json = execHttpGetJsonArray(accessToken, url + "?" + buildQueryString(params)); 101 | CedEntry[] entries = new CedEntry[json.length()]; 102 | for (int i = 0; i < json.length(); i++) { 103 | entries[i] = new CedEntry(); 104 | entries[i].title = json.getJSONObject(i).getString("title"); 105 | entries[i].digest = json.getJSONObject(i).isNull("digest") ? null : 106 | json.getJSONObject(i).optString("digest"); 107 | } 108 | return entries; 109 | } catch (Exception e) { 110 | throw new APIException(e.getMessage()); 111 | } 112 | } 113 | 114 | private static JSONArray execHttpGetJsonArray(String accessToken, String url) 115 | throws APIException { 116 | InputStream is = null; 117 | URLConnection conn = null; 118 | 119 | initProxy(); 120 | 121 | try { 122 | conn = new URL(url).openConnection(); 123 | conn.setRequestProperty("Accept", "*/*"); 124 | conn.setRequestProperty("User-Agent", "Codic IntelliJ Plugin/" + CodicPlugin.getVersion()); 125 | conn.setRequestProperty("Pragma", "no-cache"); 126 | conn.setRequestProperty("Authorization", "Bearer " + accessToken); 127 | conn.setConnectTimeout(1000); 128 | conn.setReadTimeout(2000); 129 | is = conn.getInputStream(); 130 | String rawJSON = getResponseString(is); 131 | 132 | return new JSONArray(rawJSON); 133 | } catch (IOException e) { 134 | if (is != null) { 135 | try { 136 | is.close(); 137 | } catch (IOException e1) { 138 | } 139 | } 140 | try { 141 | //int responseCode = ((HttpURLConnection) conn).getResponseCode(); 142 | is = ((HttpURLConnection) conn).getErrorStream(); 143 | String rawJSON = getResponseString(is); 144 | JSONObject json = new JSONObject(rawJSON); 145 | int code = json.getJSONArray("errors").getJSONObject(0).getInt("code"); 146 | String message = json.getJSONArray("errors").getJSONObject(0).getString("message"); 147 | throw new APIException(message, code); 148 | } catch (IOException e1) { 149 | throw new APIException(e1.getMessage()); 150 | } catch (JSONException e2) { 151 | throw new APIException(e2.getMessage()); 152 | } 153 | } catch (JSONException e2) { 154 | throw new APIException(e2.getMessage()); 155 | } finally { 156 | if (is != null) 157 | try { 158 | is.close(); 159 | } catch (IOException e) { 160 | } 161 | } 162 | } 163 | 164 | /** 165 | * Configure HTTP proxy. 166 | */ 167 | private static void initProxy() 168 | { 169 | HttpConfigurable httpConfigurable = (HttpConfigurable) 170 | ApplicationManager.getApplication().getComponent("HttpConfigurable"); 171 | 172 | if (httpConfigurable == null) { 173 | httpConfigurable = HttpConfigurable.getInstance(); 174 | } 175 | if (httpConfigurable != null) { 176 | if (httpConfigurable.USE_HTTP_PROXY) { 177 | System.getProperties().put("proxySet", Boolean.valueOf(httpConfigurable.USE_HTTP_PROXY).toString()); 178 | System.getProperties().put("proxyPort", Integer.toString(httpConfigurable.PROXY_PORT)); 179 | System.getProperties().put("proxyHost", httpConfigurable.PROXY_HOST); 180 | System.getProperties().put("http.proxySet", Boolean.valueOf(httpConfigurable.USE_HTTP_PROXY).toString()); 181 | System.getProperties().put("http.proxyPort", Integer.toString(httpConfigurable.PROXY_PORT)); 182 | System.getProperties().put("http.proxyHost", httpConfigurable.PROXY_HOST); 183 | } 184 | } 185 | } 186 | 187 | /** 188 | * @param params Query parameters. 189 | * @return Query string. 190 | */ 191 | private static String buildQueryString(Map params) { 192 | String query = ""; 193 | for (Map.Entry entry : params.entrySet()) { 194 | if (entry.getValue() != null) { 195 | if (!query.isEmpty()) 196 | query += "&"; 197 | query += encodeUTF8(entry.getKey()); 198 | query += "="; 199 | query += encodeUTF8(entry.getValue().toString()); 200 | } 201 | } 202 | return query; 203 | } 204 | 205 | /** 206 | * @param is The input stream. 207 | * @return Response as string. 208 | * @throws IOException 209 | */ 210 | private static String getResponseString(InputStream is) throws IOException { 211 | InputStreamReader isr = null; 212 | BufferedReader br = null; 213 | StringBuilder jsonString = new StringBuilder(); 214 | 215 | try { 216 | isr = new InputStreamReader(is, "UTF-8"); 217 | br = new BufferedReader(isr); 218 | int ret = 0; 219 | char[] charBuffer = new char[1024]; 220 | while ((ret = br.read(charBuffer)) > 0) { 221 | jsonString.append(charBuffer, 0, ret); 222 | } 223 | } catch (Exception e) { 224 | throw new IOException(e.getMessage(), e); 225 | } finally { 226 | if (is != null) 227 | is.close(); 228 | if (isr != null) 229 | isr.close(); 230 | if (br != null) 231 | br.close(); 232 | } 233 | return jsonString.toString(); 234 | } 235 | 236 | /** 237 | * @param value Input source. 238 | * @return URL-encoded string. 239 | */ 240 | private static String encodeUTF8(String value) { 241 | try { 242 | return URLEncoder.encode(value, "UTF-8"); 243 | } catch (UnsupportedEncodingException e) { 244 | // Suppress exception. 245 | } 246 | return null; 247 | } 248 | 249 | private static Word[] mapWords(JSONArray jsonArray) throws JSONException { 250 | Word[] words = new Word[jsonArray.length()]; 251 | for (int i = 0; i < jsonArray.length(); i++) { 252 | words[i] = new Word(); 253 | words[i].successful = jsonArray.getJSONObject(i).getBoolean("successful"); 254 | words[i].candidates = mapCandidates(jsonArray.getJSONObject(i).getJSONArray("candidates")); 255 | } 256 | return words; 257 | } 258 | 259 | private static Candidate[] mapCandidates(JSONArray jsonArray) throws JSONException { 260 | Candidate[] words = new Candidate[jsonArray.length()]; 261 | for (int i = 0; i < jsonArray.length(); i++) { 262 | words[i] = new Candidate(); 263 | words[i].text = jsonArray.getJSONObject(i).getString("text"); 264 | words[i].textInCasing = jsonArray.getJSONObject(i).getString("text_in_casing"); 265 | } 266 | return words; 267 | } 268 | 269 | 270 | } 271 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/Translation.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | public class Translation { 4 | public Word[] words; 5 | public String translatedText; 6 | public String translatedTextInCasing; 7 | public String description; 8 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/UserProject.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | public class UserProject { 4 | public Long id; 5 | public String name; 6 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/api/Word.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.api; 2 | 3 | public class Word { 4 | // public String sourceText; 5 | // public String translatedText; 6 | public Candidate[] candidates; 7 | public boolean successful; 8 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/i18n.properties: -------------------------------------------------------------------------------- 1 | preference.hint_text=Tip: After signing up for codic,
you can get the access token from the API status page. 2 | messages.access_token_required=Configure your access token in preference page. 3 | messages.api_rate_limit_exceeded=Rate limit exceeded. -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/i18n_ja.properties: -------------------------------------------------------------------------------- 1 | preference.hint_text=Tip \: \u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306Fcodic\u306B\u30B5\u30A4\u30F3\u30A2\u30C3\u30D7\u5F8C\u3001
API\u30B9\u30C6\u30FC\u30BF\u30B9\u30DA\u30FC\u30B8\u304B\u3089\u53D6\u5F97\u3067\u304D\u307E\u3059\u3002 2 | messages.access_token_required=\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u3092\u30D7\u30EC\u30D5\u30A1\u30EC\u30F3\u30B9\u304B\u3089\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 3 | messages.api_rate_limit_exceeded=API\u30EC\u30FC\u30C8\u30EA\u30DF\u30C3\u30C8\u306E\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F\uFF08codic Web\u30B5\u30A4\u30C8\u306EAPI\u30B9\u30C6\u30FC\u30BF\u30B9\u306E\u30DA\u30FC\u30B8\u3088\u308A\u30EA\u30BB\u30C3\u30C8\u3067\u304D\u307E\u3059\uFF09\u3002 -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | import java.util.ArrayList; 26 | import java.util.Collection; 27 | 28 | /** 29 | * A JSONArray is an ordered sequence of values. Its external text form is a 30 | * string wrapped in square brackets with commas separating the values. The 31 | * internal form is an object having get and opt 32 | * methods for accessing the values by index, and put methods for 33 | * adding or replacing values. The values can be any of these types: 34 | * Boolean, JSONArray, JSONObject, 35 | * Number, String, or the 36 | * JSONObject.NULL object. 37 | *

38 | * The constructor can convert a JSON text into a Java object. The 39 | * toString method converts to JSON text. 40 | *

41 | * A get method returns a value if one can be found, and throws an 42 | * exception if one cannot be found. An opt method returns a 43 | * default value instead of throwing an exception, and so is useful for 44 | * obtaining optional values. 45 | *

46 | * The generic get() and opt() methods return an 47 | * object which you can cast or query for type. There are also typed 48 | * get and opt methods that do type checking and type 49 | * coersion for you. 50 | *

51 | * The texts produced by the toString methods strictly conform to 52 | * JSON syntax rules. The constructors are more forgiving in the texts they will 53 | * accept: 54 | *

    55 | *
  • An extra , (comma) may appear just 56 | * before the closing bracket.
  • 57 | *
  • The null value will be inserted when there 58 | * is , (comma) elision.
  • 59 | *
  • Strings may be quoted with ' (single 60 | * quote).
  • 61 | *
  • Strings do not need to be quoted at all if they do not begin with a quote 62 | * or single quote, and if they do not contain leading or trailing spaces, 63 | * and if they do not contain any of these characters: 64 | * { } [ ] / \ : , = ; # and if they do not look like numbers 65 | * and if they are not the reserved words true, 66 | * false, or null.
  • 67 | *
  • Values can be separated by ; (semicolon) as 68 | * well as by , (comma).
  • 69 | *
  • Numbers may have the 0- (octal) or 70 | * 0x- (hex) prefix.
  • 71 | *
  • Comments written in the slashshlash, slashstar, and hash conventions 72 | * will be ignored.
  • 73 | *
74 | 75 | * @author JSON.org 76 | * @version 2 77 | */ 78 | public class JSONArray { 79 | 80 | 81 | /** 82 | * The arrayList where the JSONArray's properties are kept. 83 | */ 84 | private ArrayList myArrayList; 85 | 86 | 87 | /** 88 | * Construct an empty JSONArray. 89 | */ 90 | public JSONArray() { 91 | this.myArrayList = new ArrayList(); 92 | } 93 | 94 | /** 95 | * Construct a JSONArray from a JSONTokener. 96 | * @param x A JSONTokener 97 | * @throws JSONException If there is a syntax error. 98 | */ 99 | public JSONArray(JSONTokener x) throws JSONException { 100 | this(); 101 | if (x.nextClean() != '[') { 102 | throw x.syntaxError("A JSONArray text must start with '['"); 103 | } 104 | if (x.nextClean() == ']') { 105 | return; 106 | } 107 | x.back(); 108 | for (;;) { 109 | if (x.nextClean() == ',') { 110 | x.back(); 111 | this.myArrayList.add(null); 112 | } else { 113 | x.back(); 114 | this.myArrayList.add(x.nextValue()); 115 | } 116 | switch (x.nextClean()) { 117 | case ';': 118 | case ',': 119 | if (x.nextClean() == ']') { 120 | return; 121 | } 122 | x.back(); 123 | break; 124 | case ']': 125 | return; 126 | default: 127 | throw x.syntaxError("Expected a ',' or ']'"); 128 | } 129 | } 130 | } 131 | 132 | 133 | /** 134 | * Construct a JSONArray from a source sJSON text. 135 | * @param string A string that begins with 136 | * [ (left bracket) 137 | * and ends with ] (right bracket). 138 | * @throws JSONException If there is a syntax error. 139 | */ 140 | public JSONArray(String string) throws JSONException { 141 | this(new JSONTokener(string)); 142 | } 143 | 144 | 145 | /** 146 | * Construct a JSONArray from a Collection. 147 | * @param collection A Collection. 148 | */ 149 | public JSONArray(Collection collection) { 150 | this.myArrayList = new ArrayList(collection); 151 | } 152 | 153 | 154 | /** 155 | * Get the object value associated with an index. 156 | * @param index 157 | * The index must be between 0 and length() - 1. 158 | * @return An object value. 159 | * @throws JSONException If there is no value for the index. 160 | */ 161 | public Object get(int index) throws JSONException { 162 | Object o = opt(index); 163 | if (o == null) { 164 | throw new JSONException("JSONArray[" + index + "] not found."); 165 | } 166 | return o; 167 | } 168 | 169 | 170 | /** 171 | * Get the boolean value associated with an index. 172 | * The string values "true" and "false" are converted to boolean. 173 | * 174 | * @param index The index must be between 0 and length() - 1. 175 | * @return The truth. 176 | * @throws JSONException If there is no value for the index or if the 177 | * value is not convertable to boolean. 178 | */ 179 | public boolean getBoolean(int index) throws JSONException { 180 | Object o = get(index); 181 | if (o.equals(Boolean.FALSE) || 182 | (o instanceof String && 183 | ((String)o).equalsIgnoreCase("false"))) { 184 | return false; 185 | } else if (o.equals(Boolean.TRUE) || 186 | (o instanceof String && 187 | ((String)o).equalsIgnoreCase("true"))) { 188 | return true; 189 | } 190 | throw new JSONException("JSONArray[" + index + "] is not a Boolean."); 191 | } 192 | 193 | 194 | /** 195 | * Get the double value associated with an index. 196 | * 197 | * @param index The index must be between 0 and length() - 1. 198 | * @return The value. 199 | * @throws JSONException If the key is not found or if the value cannot 200 | * be converted to a number. 201 | */ 202 | public double getDouble(int index) throws JSONException { 203 | Object o = get(index); 204 | try { 205 | return o instanceof Number ? 206 | ((Number)o).doubleValue() : Double.parseDouble((String)o); 207 | } catch (Exception e) { 208 | throw new JSONException("JSONArray[" + index + 209 | "] is not a number."); 210 | } 211 | } 212 | 213 | 214 | /** 215 | * Get the int value associated with an index. 216 | * 217 | * @param index The index must be between 0 and length() - 1. 218 | * @return The value. 219 | * @throws JSONException If the key is not found or if the value cannot 220 | * be converted to a number. 221 | * if the value cannot be converted to a number. 222 | */ 223 | public int getInt(int index) throws JSONException { 224 | Object o = get(index); 225 | return o instanceof Number ? 226 | ((Number)o).intValue() : (int)getDouble(index); 227 | } 228 | 229 | 230 | /** 231 | * Get the JSONArray associated with an index. 232 | * @param index The index must be between 0 and length() - 1. 233 | * @return A JSONArray value. 234 | * @throws JSONException If there is no value for the index. or if the 235 | * value is not a JSONArray 236 | */ 237 | public JSONArray getJSONArray(int index) throws JSONException { 238 | Object o = get(index); 239 | if (o instanceof JSONArray) { 240 | return (JSONArray)o; 241 | } 242 | throw new JSONException("JSONArray[" + index + 243 | "] is not a JSONArray."); 244 | } 245 | 246 | 247 | /** 248 | * Get the JSONObject associated with an index. 249 | * @param index subscript 250 | * @return A JSONObject value. 251 | * @throws JSONException If there is no value for the index or if the 252 | * value is not a JSONObject 253 | */ 254 | public JSONObject getJSONObject(int index) throws JSONException { 255 | Object o = get(index); 256 | if (o instanceof JSONObject) { 257 | return (JSONObject)o; 258 | } 259 | throw new JSONException("JSONArray[" + index + 260 | "] is not a JSONObject."); 261 | } 262 | 263 | 264 | /** 265 | * Get the long value associated with an index. 266 | * 267 | * @param index The index must be between 0 and length() - 1. 268 | * @return The value. 269 | * @throws JSONException If the key is not found or if the value cannot 270 | * be converted to a number. 271 | */ 272 | public long getLong(int index) throws JSONException { 273 | Object o = get(index); 274 | return o instanceof Number ? 275 | ((Number)o).longValue() : (long)getDouble(index); 276 | } 277 | 278 | 279 | /** 280 | * Get the string associated with an index. 281 | * @param index The index must be between 0 and length() - 1. 282 | * @return A string value. 283 | * @throws JSONException If there is no value for the index. 284 | */ 285 | public String getString(int index) throws JSONException { 286 | return get(index).toString(); 287 | } 288 | 289 | 290 | /** 291 | * Determine if the value is null. 292 | * @param index The index must be between 0 and length() - 1. 293 | * @return true if the value at the index is null, or if there is no value. 294 | */ 295 | public boolean isNull(int index) { 296 | return JSONObject.NULL.equals(opt(index)); 297 | } 298 | 299 | 300 | /** 301 | * Make a string from the contents of this JSONArray. The 302 | * separator string is inserted between each element. 303 | * Warning: This method assumes that the data structure is acyclical. 304 | * @param separator A string that will be inserted between the elements. 305 | * @return a string. 306 | * @throws JSONException If the array contains an invalid number. 307 | */ 308 | public String join(String separator) throws JSONException { 309 | int len = length(); 310 | StringBuffer sb = new StringBuffer(); 311 | 312 | for (int i = 0; i < len; i += 1) { 313 | if (i > 0) { 314 | sb.append(separator); 315 | } 316 | sb.append(JSONObject.valueToString(this.myArrayList.get(i))); 317 | } 318 | return sb.toString(); 319 | } 320 | 321 | 322 | /** 323 | * Get the number of elements in the JSONArray, included nulls. 324 | * 325 | * @return The length (or size). 326 | */ 327 | public int length() { 328 | return this.myArrayList.size(); 329 | } 330 | 331 | 332 | /** 333 | * Get the optional object value associated with an index. 334 | * @param index The index must be between 0 and length() - 1. 335 | * @return An object value, or null if there is no 336 | * object at that index. 337 | */ 338 | public Object opt(int index) { 339 | return (index < 0 || index >= length()) ? 340 | null : this.myArrayList.get(index); 341 | } 342 | 343 | 344 | /** 345 | * Get the optional boolean value associated with an index. 346 | * It returns false if there is no value at that index, 347 | * or if the value is not Boolean.TRUE or the String "true". 348 | * 349 | * @param index The index must be between 0 and length() - 1. 350 | * @return The truth. 351 | */ 352 | public boolean optBoolean(int index) { 353 | return optBoolean(index, false); 354 | } 355 | 356 | 357 | /** 358 | * Get the optional boolean value associated with an index. 359 | * It returns the defaultValue if there is no value at that index or if 360 | * it is not a Boolean or the String "true" or "false" (case insensitive). 361 | * 362 | * @param index The index must be between 0 and length() - 1. 363 | * @param defaultValue A boolean default. 364 | * @return The truth. 365 | */ 366 | public boolean optBoolean(int index, boolean defaultValue) { 367 | try { 368 | return getBoolean(index); 369 | } catch (Exception e) { 370 | return defaultValue; 371 | } 372 | } 373 | 374 | 375 | /** 376 | * Get the optional double value associated with an index. 377 | * NaN is returned if there is no value for the index, 378 | * or if the value is not a number and cannot be converted to a number. 379 | * 380 | * @param index The index must be between 0 and length() - 1. 381 | * @return The value. 382 | */ 383 | public double optDouble(int index) { 384 | return optDouble(index, Double.NaN); 385 | } 386 | 387 | 388 | /** 389 | * Get the optional double value associated with an index. 390 | * The defaultValue is returned if there is no value for the index, 391 | * or if the value is not a number and cannot be converted to a number. 392 | * 393 | * @param index subscript 394 | * @param defaultValue The default value. 395 | * @return The value. 396 | */ 397 | public double optDouble(int index, double defaultValue) { 398 | try { 399 | return getDouble(index); 400 | } catch (Exception e) { 401 | return defaultValue; 402 | } 403 | } 404 | 405 | 406 | /** 407 | * Get the optional int value associated with an index. 408 | * Zero is returned if there is no value for the index, 409 | * or if the value is not a number and cannot be converted to a number. 410 | * 411 | * @param index The index must be between 0 and length() - 1. 412 | * @return The value. 413 | */ 414 | public int optInt(int index) { 415 | return optInt(index, 0); 416 | } 417 | 418 | 419 | /** 420 | * Get the optional int value associated with an index. 421 | * The defaultValue is returned if there is no value for the index, 422 | * or if the value is not a number and cannot be converted to a number. 423 | * @param index The index must be between 0 and length() - 1. 424 | * @param defaultValue The default value. 425 | * @return The value. 426 | */ 427 | public int optInt(int index, int defaultValue) { 428 | try { 429 | return getInt(index); 430 | } catch (Exception e) { 431 | return defaultValue; 432 | } 433 | } 434 | 435 | 436 | /** 437 | * Get the optional JSONArray associated with an index. 438 | * @param index subscript 439 | * @return A JSONArray value, or null if the index has no value, 440 | * or if the value is not a JSONArray. 441 | */ 442 | public JSONArray optJSONArray(int index) { 443 | Object o = opt(index); 444 | return o instanceof JSONArray ? (JSONArray)o : null; 445 | } 446 | 447 | 448 | /** 449 | * Get the optional JSONObject associated with an index. 450 | * Null is returned if the key is not found, or null if the index has 451 | * no value, or if the value is not a JSONObject. 452 | * 453 | * @param index The index must be between 0 and length() - 1. 454 | * @return A JSONObject value. 455 | */ 456 | public JSONObject optJSONObject(int index) { 457 | Object o = opt(index); 458 | return o instanceof JSONObject ? (JSONObject)o : null; 459 | } 460 | 461 | 462 | /** 463 | * Get the optional long value associated with an index. 464 | * Zero is returned if there is no value for the index, 465 | * or if the value is not a number and cannot be converted to a number. 466 | * 467 | * @param index The index must be between 0 and length() - 1. 468 | * @return The value. 469 | */ 470 | public long optLong(int index) { 471 | return optLong(index, 0); 472 | } 473 | 474 | 475 | /** 476 | * Get the optional long value associated with an index. 477 | * The defaultValue is returned if there is no value for the index, 478 | * or if the value is not a number and cannot be converted to a number. 479 | * @param index The index must be between 0 and length() - 1. 480 | * @param defaultValue The default value. 481 | * @return The value. 482 | */ 483 | public long optLong(int index, long defaultValue) { 484 | try { 485 | return getLong(index); 486 | } catch (Exception e) { 487 | return defaultValue; 488 | } 489 | } 490 | 491 | 492 | /** 493 | * Get the optional string value associated with an index. It returns an 494 | * empty string if there is no value at that index. If the value 495 | * is not a string and is not null, then it is coverted to a string. 496 | * 497 | * @param index The index must be between 0 and length() - 1. 498 | * @return A String value. 499 | */ 500 | public String optString(int index) { 501 | return optString(index, ""); 502 | } 503 | 504 | 505 | /** 506 | * Get the optional string associated with an index. 507 | * The defaultValue is returned if the key is not found. 508 | * 509 | * @param index The index must be between 0 and length() - 1. 510 | * @param defaultValue The default value. 511 | * @return A String value. 512 | */ 513 | public String optString(int index, String defaultValue) { 514 | Object o = opt(index); 515 | return o != null ? o.toString() : defaultValue; 516 | } 517 | 518 | 519 | /** 520 | * Append a boolean value. This increases the array's length by one. 521 | * 522 | * @param value A boolean value. 523 | * @return this. 524 | */ 525 | public JSONArray put(boolean value) { 526 | put(Boolean.valueOf(value)); 527 | return this; 528 | } 529 | 530 | 531 | /** 532 | * Append a double value. This increases the array's length by one. 533 | * 534 | * @param value A double value. 535 | * @throws JSONException if the value is not finite. 536 | * @return this. 537 | */ 538 | public JSONArray put(double value) throws JSONException { 539 | Double d = new Double(value); 540 | JSONObject.testValidity(d); 541 | put(d); 542 | return this; 543 | } 544 | 545 | 546 | /** 547 | * Append an int value. This increases the array's length by one. 548 | * 549 | * @param value An int value. 550 | * @return this. 551 | */ 552 | public JSONArray put(int value) { 553 | put(new Integer(value)); 554 | return this; 555 | } 556 | 557 | 558 | /** 559 | * Append an long value. This increases the array's length by one. 560 | * 561 | * @param value A long value. 562 | * @return this. 563 | */ 564 | public JSONArray put(long value) { 565 | put(new Long(value)); 566 | return this; 567 | } 568 | 569 | 570 | /** 571 | * Append an object value. This increases the array's length by one. 572 | * @param value An object value. The value should be a 573 | * Boolean, Double, Integer, JSONArray, JSObject, Long, or String, or the 574 | * JSONObject.NULL object. 575 | * @return this. 576 | */ 577 | public JSONArray put(Object value) { 578 | this.myArrayList.add(value); 579 | return this; 580 | } 581 | 582 | 583 | /** 584 | * Put or replace a boolean value in the JSONArray. If the index is greater 585 | * than the length of the JSONArray, then null elements will be added as 586 | * necessary to pad it out. 587 | * @param index The subscript. 588 | * @param value A boolean value. 589 | * @return this. 590 | * @throws JSONException If the index is negative. 591 | */ 592 | public JSONArray put(int index, boolean value) throws JSONException { 593 | put(index, Boolean.valueOf(value)); 594 | return this; 595 | } 596 | 597 | 598 | /** 599 | * Put or replace a double value. If the index is greater than the length of 600 | * the JSONArray, then null elements will be added as necessary to pad 601 | * it out. 602 | * @param index The subscript. 603 | * @param value A double value. 604 | * @return this. 605 | * @throws JSONException If the index is negative or if the value is 606 | * not finite. 607 | */ 608 | public JSONArray put(int index, double value) throws JSONException { 609 | put(index, new Double(value)); 610 | return this; 611 | } 612 | 613 | 614 | /** 615 | * Put or replace an int value. If the index is greater than the length of 616 | * the JSONArray, then null elements will be added as necessary to pad 617 | * it out. 618 | * @param index The subscript. 619 | * @param value An int value. 620 | * @return this. 621 | * @throws JSONException If the index is negative. 622 | */ 623 | public JSONArray put(int index, int value) throws JSONException { 624 | put(index, new Integer(value)); 625 | return this; 626 | } 627 | 628 | 629 | /** 630 | * Put or replace a long value. If the index is greater than the length of 631 | * the JSONArray, then null elements will be added as necessary to pad 632 | * it out. 633 | * @param index The subscript. 634 | * @param value A long value. 635 | * @return this. 636 | * @throws JSONException If the index is negative. 637 | */ 638 | public JSONArray put(int index, long value) throws JSONException { 639 | put(index, new Long(value)); 640 | return this; 641 | } 642 | 643 | 644 | /** 645 | * Put or replace an object value in the JSONArray. If the index is greater 646 | * than the length of the JSONArray, then null elements will be added as 647 | * necessary to pad it out. 648 | * @param index The subscript. 649 | * @param value The value to put into the array. 650 | * @return this. 651 | * @throws JSONException If the index is negative or if the the value is 652 | * an invalid number. 653 | */ 654 | public JSONArray put(int index, Object value) throws JSONException { 655 | JSONObject.testValidity(value); 656 | if (index < 0) { 657 | throw new JSONException("JSONArray[" + index + "] not found."); 658 | } 659 | if (index < length()) { 660 | this.myArrayList.set(index, value); 661 | } else { 662 | while (index != length()) { 663 | put(null); 664 | } 665 | put(value); 666 | } 667 | return this; 668 | } 669 | 670 | 671 | /** 672 | * Produce a JSONObject by combining a JSONArray of names with the values 673 | * of this JSONArray. 674 | * @param names A JSONArray containing a list of key strings. These will be 675 | * paired with the values. 676 | * @return A JSONObject, or null if there are no names or if this JSONArray 677 | * has no values. 678 | * @throws JSONException If any of the names are null. 679 | */ 680 | public JSONObject toJSONObject(JSONArray names) throws JSONException { 681 | if (names == null || names.length() == 0 || length() == 0) { 682 | return null; 683 | } 684 | JSONObject jo = new JSONObject(); 685 | for (int i = 0; i < names.length(); i += 1) { 686 | jo.put(names.getString(i), this.opt(i)); 687 | } 688 | return jo; 689 | } 690 | 691 | 692 | /** 693 | * Make an JSON text of this JSONArray. For compactness, no 694 | * unnecessary whitespace is added. If it is not possible to produce a 695 | * syntactically correct JSON text then null will be returned instead. This 696 | * could occur if the array contains an invalid number. 697 | *

698 | * Warning: This method assumes that the data structure is acyclical. 699 | * 700 | * @return a printable, displayable, transmittable 701 | * representation of the array. 702 | */ 703 | public String toString() { 704 | try { 705 | return '[' + join(",") + ']'; 706 | } catch (Exception e) { 707 | return null; 708 | } 709 | } 710 | 711 | 712 | /** 713 | * Make a prettyprinted JSON text of this JSONArray. 714 | * Warning: This method assumes that the data structure is acyclical. 715 | * @param indentFactor The number of spaces to add to each level of 716 | * indentation. 717 | * @return a printable, displayable, transmittable 718 | * representation of the object, beginning 719 | * with [ (left bracket) and ending 720 | * with ] (right bracket). 721 | * @throws JSONException 722 | */ 723 | public String toString(int indentFactor) throws JSONException { 724 | return toString(indentFactor, 0); 725 | } 726 | 727 | 728 | /** 729 | * Make a prettyprinted JSON text of this JSONArray. 730 | * Warning: This method assumes that the data structure is acyclical. 731 | * @param indentFactor The number of spaces to add to each level of 732 | * indentation. 733 | * @param indent The indention of the top level. 734 | * @return a printable, displayable, transmittable 735 | * representation of the array. 736 | * @throws JSONException 737 | */ 738 | String toString(int indentFactor, int indent) throws JSONException { 739 | int len = length(); 740 | if (len == 0) { 741 | return "[]"; 742 | } 743 | int i; 744 | StringBuffer sb = new StringBuffer("["); 745 | if (len == 1) { 746 | sb.append(JSONObject.valueToString(this.myArrayList.get(0), 747 | indentFactor, indent)); 748 | } else { 749 | int newindent = indent + indentFactor; 750 | sb.append('\n'); 751 | for (i = 0; i < len; i += 1) { 752 | if (i > 0) { 753 | sb.append(",\n"); 754 | } 755 | for (int j = 0; j < newindent; j += 1) { 756 | sb.append(' '); 757 | } 758 | sb.append(JSONObject.valueToString(this.myArrayList.get(i), 759 | indentFactor, newindent)); 760 | } 761 | sb.append('\n'); 762 | for (i = 0; i < indent; i += 1) { 763 | sb.append(' '); 764 | } 765 | } 766 | sb.append(']'); 767 | return sb.toString(); 768 | } 769 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | 26 | /** 27 | * The JSONException is thrown by the JSON.org classes then things are amiss. 28 | * @author JSON.org 29 | * @version 2 30 | */ 31 | public class JSONException extends Exception { 32 | static final long serialVersionUID = -3387516993124229949L; 33 | 34 | /** 35 | * Constructs a JSONException with an explanatory message. 36 | * @param message Detail about the reason for the exception. 37 | */ 38 | public JSONException(String message) { 39 | super(message); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | import java.util.HashMap; 26 | import java.util.Iterator; 27 | import java.util.Map; 28 | 29 | /** 30 | * A JSONObject is an unordered collection of name/value pairs. Its 31 | * external form is a string wrapped in curly braces with colons between the 32 | * names and values, and commas between the values and names. The internal form 33 | * is an object having get and opt methods for 34 | * accessing the values by name, and put methods for adding or 35 | * replacing values by name. The values can be any of these types: 36 | * Boolean, JSONArray, JSONObject, 37 | * Number, String, or the JSONObject.NULL 38 | * object. A JSONObject constructor can be used to convert an external form 39 | * JSON text into an internal form whose values can be retrieved with the 40 | * get and opt methods, or to convert values into a 41 | * JSON text using the put and toString methods. 42 | * A get method returns a value if one can be found, and throws an 43 | * exception if one cannot be found. An opt method returns a 44 | * default value instead of throwing an exception, and so is useful for 45 | * obtaining optional values. 46 | *

47 | * The generic get() and opt() methods return an 48 | * object, which you can cast or query for type. There are also typed 49 | * get and opt methods that do type checking and type 50 | * coersion for you. 51 | *

52 | * The put methods adds values to an object. For example,

  53 |  *     myString = new JSONObject().put("JSON", "Hello, World!").toString();
54 | * produces the string {"JSON": "Hello, World"}. 55 | *

56 | * The texts produced by the toString methods strictly conform to 57 | * the JSON sysntax rules. 58 | * The constructors are more forgiving in the texts they will accept: 59 | *

    60 | *
  • An extra , (comma) may appear just 61 | * before the closing brace.
  • 62 | *
  • Strings may be quoted with ' (single 63 | * quote).
  • 64 | *
  • Strings do not need to be quoted at all if they do not begin with a quote 65 | * or single quote, and if they do not contain leading or trailing spaces, 66 | * and if they do not contain any of these characters: 67 | * { } [ ] / \ : , = ; # and if they do not look like numbers 68 | * and if they are not the reserved words true, 69 | * false, or null.
  • 70 | *
  • Keys can be followed by = or => as well as 71 | * by :.
  • 72 | *
  • Values can be followed by ; (semicolon) as 73 | * well as by , (comma).
  • 74 | *
  • Numbers may have the 0- (octal) or 75 | * 0x- (hex) prefix.
  • 76 | *
  • Comments written in the slashshlash, slashstar, and hash conventions 77 | * will be ignored.
  • 78 | *
79 | * @author JSON.org 80 | * @version 2 81 | */ 82 | public class JSONObject { 83 | 84 | /** 85 | * JSONObject.NULL is equivalent to the value that JavaScript calls null, 86 | * whilst Java's null is equivalent to the value that JavaScript calls 87 | * undefined. 88 | */ 89 | private static final class Null { 90 | 91 | /** 92 | * There is only intended to be a single instance of the NULL object, 93 | * so the clone method returns itself. 94 | * @return NULL. 95 | */ 96 | protected final Object clone() { 97 | return this; 98 | } 99 | 100 | 101 | /** 102 | * A Null object is equal to the null value and to itself. 103 | * @param object An object to test for nullness. 104 | * @return true if the object parameter is the JSONObject.NULL object 105 | * or null. 106 | */ 107 | public boolean equals(Object object) { 108 | return object == null || object == this; 109 | } 110 | 111 | 112 | /** 113 | * Get the "null" string value. 114 | * @return The string "null". 115 | */ 116 | public String toString() { 117 | return "null"; 118 | } 119 | } 120 | 121 | 122 | /** 123 | * The hash map where the JSONObject's properties are kept. 124 | */ 125 | private HashMap myHashMap; 126 | 127 | 128 | /** 129 | * It is sometimes more convenient and less ambiguous to have a 130 | * NULL object than to use Java's null value. 131 | * JSONObject.NULL.equals(null) returns true. 132 | * JSONObject.NULL.toString() returns "null". 133 | */ 134 | public static final Object NULL = new Null(); 135 | 136 | 137 | /** 138 | * Construct an empty JSONObject. 139 | */ 140 | public JSONObject() { 141 | this.myHashMap = new HashMap(); 142 | } 143 | 144 | 145 | /** 146 | * Construct a JSONObject from a subset of another JSONObject. 147 | * An array of strings is used to identify the keys that should be copied. 148 | * Missing keys are ignored. 149 | * @param jo A JSONObject. 150 | * @param sa An array of strings. 151 | * @exception JSONException If a value is a non-finite number. 152 | */ 153 | public JSONObject(JSONObject jo, String[] sa) throws JSONException { 154 | this(); 155 | for (int i = 0; i < sa.length; i += 1) { 156 | putOpt(sa[i], jo.opt(sa[i])); 157 | } 158 | } 159 | 160 | 161 | /** 162 | * Construct a JSONObject from a JSONTokener. 163 | * @param x A JSONTokener object containing the source string. 164 | * @throws JSONException If there is a syntax error in the source string. 165 | */ 166 | public JSONObject(JSONTokener x) throws JSONException { 167 | this(); 168 | char c; 169 | String key; 170 | 171 | if (x.nextClean() != '{') { 172 | throw x.syntaxError("A JSONObject text must begin with '{'"); 173 | } 174 | for (;;) { 175 | c = x.nextClean(); 176 | switch (c) { 177 | case 0: 178 | throw x.syntaxError("A JSONObject text must end with '}'"); 179 | case '}': 180 | return; 181 | default: 182 | x.back(); 183 | key = x.nextValue().toString(); 184 | } 185 | 186 | /* 187 | * The key is followed by ':'. We will also tolerate '=' or '=>'. 188 | */ 189 | 190 | c = x.nextClean(); 191 | if (c == '=') { 192 | if (x.next() != '>') { 193 | x.back(); 194 | } 195 | } else if (c != ':') { 196 | throw x.syntaxError("Expected a ':' after a key"); 197 | } 198 | this.myHashMap.put(key, x.nextValue()); 199 | 200 | /* 201 | * Pairs are separated by ','. We will also tolerate ';'. 202 | */ 203 | 204 | switch (x.nextClean()) { 205 | case ';': 206 | case ',': 207 | if (x.nextClean() == '}') { 208 | return; 209 | } 210 | x.back(); 211 | break; 212 | case '}': 213 | return; 214 | default: 215 | throw x.syntaxError("Expected a ',' or '}'"); 216 | } 217 | } 218 | } 219 | 220 | 221 | /** 222 | * Construct a JSONObject from a Map. 223 | * @param map A map object that can be used to initialize the contents of 224 | * the JSONObject. 225 | */ 226 | public JSONObject(Map map) { 227 | this.myHashMap = new HashMap(map); 228 | } 229 | 230 | 231 | /** 232 | * Construct a JSONObject from a string. 233 | * This is the most commonly used JSONObject constructor. 234 | * @param string A string beginning 235 | * with { (left brace) and ending 236 | * with } (right brace). 237 | * @exception JSONException If there is a syntax error in the source string. 238 | */ 239 | public JSONObject(String string) throws JSONException { 240 | this(new JSONTokener(string)); 241 | } 242 | 243 | 244 | /** 245 | * Accumulate values under a key. It is similar to the put method except 246 | * that if there is already an object stored under the key then a 247 | * JSONArray is stored under the key to hold all of the accumulated values. 248 | * If there is already a JSONArray, then the new value is appended to it. 249 | * In contrast, the put method replaces the previous value. 250 | * @param key A key string. 251 | * @param value An object to be accumulated under the key. 252 | * @return this. 253 | * @throws JSONException If the value is an invalid number 254 | * or if the key is null. 255 | */ 256 | public JSONObject accumulate(String key, Object value) 257 | throws JSONException { 258 | testValidity(value); 259 | Object o = opt(key); 260 | if (o == null) { 261 | put(key, value); 262 | } else if (o instanceof JSONArray) { 263 | ((JSONArray)o).put(value); 264 | } else { 265 | put(key, new JSONArray().put(o).put(value)); 266 | } 267 | return this; 268 | } 269 | 270 | 271 | /** 272 | * Get the value object associated with a key. 273 | * 274 | * @param key A key string. 275 | * @return The object associated with the key. 276 | * @throws JSONException if the key is not found. 277 | */ 278 | public Object get(String key) throws JSONException { 279 | Object o = opt(key); 280 | if (o == null) { 281 | throw new JSONException("JSONObject[" + quote(key) + 282 | "] not found."); 283 | } 284 | return o; 285 | } 286 | 287 | 288 | /** 289 | * Get the boolean value associated with a key. 290 | * 291 | * @param key A key string. 292 | * @return The truth. 293 | * @throws JSONException 294 | * if the value is not a Boolean or the String "true" or "false". 295 | */ 296 | public boolean getBoolean(String key) throws JSONException { 297 | Object o = get(key); 298 | if (o.equals(Boolean.FALSE) || 299 | (o instanceof String && 300 | ((String)o).equalsIgnoreCase("false"))) { 301 | return false; 302 | } else if (o.equals(Boolean.TRUE) || 303 | (o instanceof String && 304 | ((String)o).equalsIgnoreCase("true"))) { 305 | return true; 306 | } 307 | throw new JSONException("JSONObject[" + quote(key) + 308 | "] is not a Boolean."); 309 | } 310 | 311 | 312 | /** 313 | * Get the double value associated with a key. 314 | * @param key A key string. 315 | * @return The numeric value. 316 | * @throws JSONException if the key is not found or 317 | * if the value is not a Number object and cannot be converted to a number. 318 | */ 319 | public double getDouble(String key) throws JSONException { 320 | Object o = get(key); 321 | try { 322 | return o instanceof Number ? 323 | ((Number)o).doubleValue() : Double.parseDouble((String)o); 324 | } catch (Exception e) { 325 | throw new JSONException("JSONObject[" + quote(key) + 326 | "] is not a number."); 327 | } 328 | } 329 | 330 | 331 | /** 332 | * Get the int value associated with a key. If the number value is too 333 | * large for an int, it will be clipped. 334 | * 335 | * @param key A key string. 336 | * @return The integer value. 337 | * @throws JSONException if the key is not found or if the value cannot 338 | * be converted to an integer. 339 | */ 340 | public int getInt(String key) throws JSONException { 341 | Object o = get(key); 342 | return o instanceof Number ? 343 | ((Number)o).intValue() : (int)getDouble(key); 344 | } 345 | 346 | 347 | /** 348 | * Get the JSONArray value associated with a key. 349 | * 350 | * @param key A key string. 351 | * @return A JSONArray which is the value. 352 | * @throws JSONException if the key is not found or 353 | * if the value is not a JSONArray. 354 | */ 355 | public JSONArray getJSONArray(String key) throws JSONException { 356 | Object o = get(key); 357 | if (o instanceof JSONArray) { 358 | return (JSONArray)o; 359 | } 360 | throw new JSONException("JSONObject[" + quote(key) + 361 | "] is not a JSONArray."); 362 | } 363 | 364 | 365 | /** 366 | * Get the JSONObject value associated with a key. 367 | * 368 | * @param key A key string. 369 | * @return A JSONObject which is the value. 370 | * @throws JSONException if the key is not found or 371 | * if the value is not a JSONObject. 372 | */ 373 | public JSONObject getJSONObject(String key) throws JSONException { 374 | Object o = get(key); 375 | if (o instanceof JSONObject) { 376 | return (JSONObject)o; 377 | } 378 | throw new JSONException("JSONObject[" + quote(key) + 379 | "] is not a JSONObject."); 380 | } 381 | 382 | 383 | /** 384 | * Get the long value associated with a key. If the number value is too 385 | * long for a long, it will be clipped. 386 | * 387 | * @param key A key string. 388 | * @return The long value. 389 | * @throws JSONException if the key is not found or if the value cannot 390 | * be converted to a long. 391 | */ 392 | public long getLong(String key) throws JSONException { 393 | Object o = get(key); 394 | return o instanceof Number ? 395 | ((Number)o).longValue() : (long)getDouble(key); 396 | } 397 | 398 | 399 | /** 400 | * Get the string associated with a key. 401 | * 402 | * @param key A key string. 403 | * @return A string which is the value. 404 | * @throws JSONException if the key is not found. 405 | */ 406 | public String getString(String key) throws JSONException { 407 | return get(key).toString(); 408 | } 409 | 410 | 411 | /** 412 | * Determine if the JSONObject contains a specific key. 413 | * @param key A key string. 414 | * @return true if the key exists in the JSONObject. 415 | */ 416 | public boolean has(String key) { 417 | return this.myHashMap.containsKey(key); 418 | } 419 | 420 | 421 | /** 422 | * Determine if the value associated with the key is null or if there is 423 | * no value. 424 | * @param key A key string. 425 | * @return true if there is no value associated with the key or if 426 | * the value is the JSONObject.NULL object. 427 | */ 428 | public boolean isNull(String key) { 429 | return JSONObject.NULL.equals(opt(key)); 430 | } 431 | 432 | 433 | /** 434 | * Get an enumeration of the keys of the JSONObject. 435 | * 436 | * @return An iterator of the keys. 437 | */ 438 | public Iterator keys() { 439 | return this.myHashMap.keySet().iterator(); 440 | } 441 | 442 | 443 | /** 444 | * Get the number of keys stored in the JSONObject. 445 | * 446 | * @return The number of keys in the JSONObject. 447 | */ 448 | public int length() { 449 | return this.myHashMap.size(); 450 | } 451 | 452 | 453 | /** 454 | * Produce a JSONArray containing the names of the elements of this 455 | * JSONObject. 456 | * @return A JSONArray containing the key strings, or null if the JSONObject 457 | * is empty. 458 | */ 459 | public JSONArray names() { 460 | JSONArray ja = new JSONArray(); 461 | Iterator keys = keys(); 462 | while (keys.hasNext()) { 463 | ja.put(keys.next()); 464 | } 465 | return ja.length() == 0 ? null : ja; 466 | } 467 | 468 | /** 469 | * Produce a string from a number. 470 | * @param n A Number 471 | * @return A String. 472 | * @throws JSONException If n is a non-finite number. 473 | */ 474 | static public String numberToString(Number n) 475 | throws JSONException { 476 | if (n == null) { 477 | throw new JSONException("Null pointer"); 478 | } 479 | testValidity(n); 480 | 481 | // Shave off trailing zeros and decimal point, if possible. 482 | 483 | String s = n.toString(); 484 | if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { 485 | while (s.endsWith("0")) { 486 | s = s.substring(0, s.length() - 1); 487 | } 488 | if (s.endsWith(".")) { 489 | s = s.substring(0, s.length() - 1); 490 | } 491 | } 492 | return s; 493 | } 494 | 495 | 496 | /** 497 | * Get an optional value associated with a key. 498 | * @param key A key string. 499 | * @return An object which is the value, or null if there is no value. 500 | */ 501 | public Object opt(String key) { 502 | return key == null ? null : this.myHashMap.get(key); 503 | } 504 | 505 | 506 | /** 507 | * Get an optional boolean associated with a key. 508 | * It returns false if there is no such key, or if the value is not 509 | * Boolean.TRUE or the String "true". 510 | * 511 | * @param key A key string. 512 | * @return The truth. 513 | */ 514 | public boolean optBoolean(String key) { 515 | return optBoolean(key, false); 516 | } 517 | 518 | 519 | /** 520 | * Get an optional boolean associated with a key. 521 | * It returns the defaultValue if there is no such key, or if it is not 522 | * a Boolean or the String "true" or "false" (case insensitive). 523 | * 524 | * @param key A key string. 525 | * @param defaultValue The default. 526 | * @return The truth. 527 | */ 528 | public boolean optBoolean(String key, boolean defaultValue) { 529 | try { 530 | return getBoolean(key); 531 | } catch (Exception e) { 532 | return defaultValue; 533 | } 534 | } 535 | 536 | 537 | /** 538 | * Get an optional double associated with a key, 539 | * or NaN if there is no such key or if its value is not a number. 540 | * If the value is a string, an attempt will be made to evaluate it as 541 | * a number. 542 | * 543 | * @param key A string which is the key. 544 | * @return An object which is the value. 545 | */ 546 | public double optDouble(String key) { 547 | return optDouble(key, Double.NaN); 548 | } 549 | 550 | 551 | /** 552 | * Get an optional double associated with a key, or the 553 | * defaultValue if there is no such key or if its value is not a number. 554 | * If the value is a string, an attempt will be made to evaluate it as 555 | * a number. 556 | * 557 | * @param key A key string. 558 | * @param defaultValue The default. 559 | * @return An object which is the value. 560 | */ 561 | public double optDouble(String key, double defaultValue) { 562 | try { 563 | Object o = opt(key); 564 | return o instanceof Number ? ((Number)o).doubleValue() : 565 | new Double((String)o).doubleValue(); 566 | } catch (Exception e) { 567 | return defaultValue; 568 | } 569 | } 570 | 571 | 572 | /** 573 | * Get an optional int value associated with a key, 574 | * or zero if there is no such key or if the value is not a number. 575 | * If the value is a string, an attempt will be made to evaluate it as 576 | * a number. 577 | * 578 | * @param key A key string. 579 | * @return An object which is the value. 580 | */ 581 | public int optInt(String key) { 582 | return optInt(key, 0); 583 | } 584 | 585 | 586 | /** 587 | * Get an optional int value associated with a key, 588 | * or the default if there is no such key or if the value is not a number. 589 | * If the value is a string, an attempt will be made to evaluate it as 590 | * a number. 591 | * 592 | * @param key A key string. 593 | * @param defaultValue The default. 594 | * @return An object which is the value. 595 | */ 596 | public int optInt(String key, int defaultValue) { 597 | try { 598 | return getInt(key); 599 | } catch (Exception e) { 600 | return defaultValue; 601 | } 602 | } 603 | 604 | 605 | /** 606 | * Get an optional JSONArray associated with a key. 607 | * It returns null if there is no such key, or if its value is not a 608 | * JSONArray. 609 | * 610 | * @param key A key string. 611 | * @return A JSONArray which is the value. 612 | */ 613 | public JSONArray optJSONArray(String key) { 614 | Object o = opt(key); 615 | return o instanceof JSONArray ? (JSONArray)o : null; 616 | } 617 | 618 | 619 | /** 620 | * Get an optional JSONObject associated with a key. 621 | * It returns null if there is no such key, or if its value is not a 622 | * JSONObject. 623 | * 624 | * @param key A key string. 625 | * @return A JSONObject which is the value. 626 | */ 627 | public JSONObject optJSONObject(String key) { 628 | Object o = opt(key); 629 | return o instanceof JSONObject ? (JSONObject)o : null; 630 | } 631 | 632 | 633 | /** 634 | * Get an optional long value associated with a key, 635 | * or zero if there is no such key or if the value is not a number. 636 | * If the value is a string, an attempt will be made to evaluate it as 637 | * a number. 638 | * 639 | * @param key A key string. 640 | * @return An object which is the value. 641 | */ 642 | public long optLong(String key) { 643 | return optLong(key, 0); 644 | } 645 | 646 | 647 | /** 648 | * Get an optional long value associated with a key, 649 | * or the default if there is no such key or if the value is not a number. 650 | * If the value is a string, an attempt will be made to evaluate it as 651 | * a number. 652 | * 653 | * @param key A key string. 654 | * @param defaultValue The default. 655 | * @return An object which is the value. 656 | */ 657 | public long optLong(String key, long defaultValue) { 658 | try { 659 | return getLong(key); 660 | } catch (Exception e) { 661 | return defaultValue; 662 | } 663 | } 664 | 665 | 666 | /** 667 | * Get an optional string associated with a key. 668 | * It returns an empty string if there is no such key. If the value is not 669 | * a string and is not null, then it is coverted to a string. 670 | * 671 | * @param key A key string. 672 | * @return A string which is the value. 673 | */ 674 | public String optString(String key) { 675 | return optString(key, ""); 676 | } 677 | 678 | 679 | /** 680 | * Get an optional string associated with a key. 681 | * It returns the defaultValue if there is no such key. 682 | * 683 | * @param key A key string. 684 | * @param defaultValue The default. 685 | * @return A string which is the value. 686 | */ 687 | public String optString(String key, String defaultValue) { 688 | Object o = opt(key); 689 | return o != null ? o.toString() : defaultValue; 690 | } 691 | 692 | 693 | /** 694 | * Put a key/boolean pair in the JSONObject. 695 | * 696 | * @param key A key string. 697 | * @param value A boolean which is the value. 698 | * @return this. 699 | * @throws JSONException If the key is null. 700 | */ 701 | public JSONObject put(String key, boolean value) throws JSONException { 702 | put(key, Boolean.valueOf(value)); 703 | return this; 704 | } 705 | 706 | 707 | /** 708 | * Put a key/double pair in the JSONObject. 709 | * 710 | * @param key A key string. 711 | * @param value A double which is the value. 712 | * @return this. 713 | * @throws JSONException If the key is null or if the number is invalid. 714 | */ 715 | public JSONObject put(String key, double value) throws JSONException { 716 | put(key, new Double(value)); 717 | return this; 718 | } 719 | 720 | 721 | /** 722 | * Put a key/int pair in the JSONObject. 723 | * 724 | * @param key A key string. 725 | * @param value An int which is the value. 726 | * @return this. 727 | * @throws JSONException If the key is null. 728 | */ 729 | public JSONObject put(String key, int value) throws JSONException { 730 | put(key, new Integer(value)); 731 | return this; 732 | } 733 | 734 | 735 | /** 736 | * Put a key/long pair in the JSONObject. 737 | * 738 | * @param key A key string. 739 | * @param value A long which is the value. 740 | * @return this. 741 | * @throws JSONException If the key is null. 742 | */ 743 | public JSONObject put(String key, long value) throws JSONException { 744 | put(key, new Long(value)); 745 | return this; 746 | } 747 | 748 | 749 | /** 750 | * Put a key/value pair in the JSONObject. If the value is null, 751 | * then the key will be removed from the JSONObject if it is present. 752 | * @param key A key string. 753 | * @param value An object which is the value. It should be of one of these 754 | * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, 755 | * or the JSONObject.NULL object. 756 | * @return this. 757 | * @throws JSONException If the value is non-finite number 758 | * or if the key is null. 759 | */ 760 | public JSONObject put(String key, Object value) 761 | throws JSONException { 762 | if (key == null) { 763 | throw new JSONException("Null key."); 764 | } 765 | if (value != null) { 766 | testValidity(value); 767 | this.myHashMap.put(key, value); 768 | } else { 769 | remove(key); 770 | } 771 | return this; 772 | } 773 | 774 | 775 | /** 776 | * Put a key/value pair in the JSONObject, but only if the 777 | * key and the value are both non-null. 778 | * @param key A key string. 779 | * @param value An object which is the value. It should be of one of these 780 | * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, 781 | * or the JSONObject.NULL object. 782 | * @return this. 783 | * @throws JSONException If the value is a non-finite number. 784 | */ 785 | public JSONObject putOpt(String key, Object value) throws JSONException { 786 | if (key != null && value != null) { 787 | put(key, value); 788 | } 789 | return this; 790 | } 791 | 792 | 793 | /** 794 | * Produce a string in double quotes with backslash sequences in all the 795 | * right places. A backslash will be inserted within 913 | * Warning: This method assumes that the data structure is acyclical. 914 | * 915 | * @return a printable, displayable, portable, transmittable 916 | * representation of the object, beginning 917 | * with { (left brace) and ending 918 | * with } (right brace). 919 | */ 920 | public String toString() { 921 | try { 922 | Iterator keys = keys(); 923 | StringBuffer sb = new StringBuffer("{"); 924 | 925 | while (keys.hasNext()) { 926 | if (sb.length() > 1) { 927 | sb.append(','); 928 | } 929 | Object o = keys.next(); 930 | sb.append(quote(o.toString())); 931 | sb.append(':'); 932 | sb.append(valueToString(this.myHashMap.get(o))); 933 | } 934 | sb.append('}'); 935 | return sb.toString(); 936 | } catch (Exception e) { 937 | return null; 938 | } 939 | } 940 | 941 | 942 | /** 943 | * Make a prettyprinted JSON text of this JSONObject. 944 | *

945 | * Warning: This method assumes that the data structure is acyclical. 946 | * @param indentFactor The number of spaces to add to each level of 947 | * indentation. 948 | * @return a printable, displayable, portable, transmittable 949 | * representation of the object, beginning 950 | * with { (left brace) and ending 951 | * with } (right brace). 952 | * @throws JSONException If the object contains an invalid number. 953 | */ 954 | public String toString(int indentFactor) throws JSONException { 955 | return toString(indentFactor, 0); 956 | } 957 | 958 | 959 | /** 960 | * Make a prettyprinted JSON text of this JSONObject. 961 | *

962 | * Warning: This method assumes that the data structure is acyclical. 963 | * @param indentFactor The number of spaces to add to each level of 964 | * indentation. 965 | * @param indent The indentation of the top level. 966 | * @return a printable, displayable, transmittable 967 | * representation of the object, beginning 968 | * with { (left brace) and ending 969 | * with } (right brace). 970 | * @throws JSONException If the object contains an invalid number. 971 | */ 972 | String toString(int indentFactor, int indent) throws JSONException { 973 | int i; 974 | int n = length(); 975 | if (n == 0) { 976 | return "{}"; 977 | } 978 | Iterator keys = keys(); 979 | StringBuffer sb = new StringBuffer("{"); 980 | int newindent = indent + indentFactor; 981 | Object o; 982 | if (n == 1) { 983 | o = keys.next(); 984 | sb.append(quote(o.toString())); 985 | sb.append(": "); 986 | sb.append(valueToString(this.myHashMap.get(o), indentFactor, 987 | indent)); 988 | } else { 989 | while (keys.hasNext()) { 990 | o = keys.next(); 991 | if (sb.length() > 1) { 992 | sb.append(",\n"); 993 | } else { 994 | sb.append('\n'); 995 | } 996 | for (i = 0; i < newindent; i += 1) { 997 | sb.append(' '); 998 | } 999 | sb.append(quote(o.toString())); 1000 | sb.append(": "); 1001 | sb.append(valueToString(this.myHashMap.get(o), indentFactor, 1002 | newindent)); 1003 | } 1004 | if (sb.length() > 1) { 1005 | sb.append('\n'); 1006 | for (i = 0; i < indent; i += 1) { 1007 | sb.append(' '); 1008 | } 1009 | } 1010 | } 1011 | sb.append('}'); 1012 | return sb.toString(); 1013 | } 1014 | 1015 | 1016 | /** 1017 | * Make a JSON text of an object value. 1018 | *

1019 | * Warning: This method assumes that the data structure is acyclical. 1020 | * @param value The value to be serialized. 1021 | * @return a printable, displayable, transmittable 1022 | * representation of the object, beginning 1023 | * with { (left brace) and ending 1024 | * with } (right brace). 1025 | * @throws JSONException If the value is or contains an invalid number. 1026 | */ 1027 | static String valueToString(Object value) throws JSONException { 1028 | if (value == null) { 1029 | return "null"; 1030 | } 1031 | if (value instanceof Number) { 1032 | return numberToString((Number) value); 1033 | } 1034 | if (value instanceof Boolean || value instanceof JSONObject || 1035 | value instanceof JSONArray) { 1036 | return value.toString(); 1037 | } 1038 | return quote(value.toString()); 1039 | } 1040 | 1041 | 1042 | /** 1043 | * Make a prettyprinted JSON text of an object value. 1044 | *

1045 | * Warning: This method assumes that the data structure is acyclical. 1046 | * @param value The value to be serialized. 1047 | * @param indentFactor The number of spaces to add to each level of 1048 | * indentation. 1049 | * @param indent The indentation of the top level. 1050 | * @return a printable, displayable, transmittable 1051 | * representation of the object, beginning 1052 | * with { (left brace) and ending 1053 | * with } (right brace). 1054 | * @throws JSONException If the object contains an invalid number. 1055 | */ 1056 | static String valueToString(Object value, int indentFactor, int indent) 1057 | throws JSONException { 1058 | if (value == null) { 1059 | return "null"; 1060 | } 1061 | if (value instanceof Number) { 1062 | return numberToString((Number) value); 1063 | } 1064 | if (value instanceof Boolean) { 1065 | return value.toString(); 1066 | } 1067 | if (value instanceof JSONObject) { 1068 | return ((JSONObject)value).toString(indentFactor, indent); 1069 | } 1070 | if (value instanceof JSONArray) { 1071 | return ((JSONArray)value).toString(indentFactor, indent); 1072 | } 1073 | return quote(value.toString()); 1074 | } 1075 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONStringer.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2005 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | 26 | /** 27 | * JSONStringer provides a quick and convenient way of producing JSON text. 28 | * The texts produced strictly conform to JSON syntax rules. No whitespace is 29 | * added, so the results are ready for transmission or storage. Each instance of 30 | * JSONStringer can produce one JSON text. 31 | *

32 | * A JSONStringer instance provides a value method for appending 33 | * values to the 34 | * text, and a key 35 | * method for adding keys before values in objects. There are array 36 | * and endArray methods that make and bound array values, and 37 | * object and endObject methods which make and bound 38 | * object values. All of these methods return the JSONStringer instance, 39 | * permitting cascade style. For example,

 40 |  * myString = new JSONStringer()
 41 |  *     .object()
 42 |  *         .key("JSON").value("Hello, World!")
 43 |  *     .endObject()
 44 |  *     .toString();
which produces the string
 45 |  * {"JSON":"Hello, World!"}
46 | *

47 | * The first method called must be array or object. 48 | * There are no methods for adding commas or colons. JSONStringer adds them for 49 | * you. Objects and arrays can be nested up to 20 levels deep. 50 | *

51 | * This can sometimes be easier than using a JSONObject to build a string. 52 | * @author JSON.org 53 | * @version 2 54 | */ 55 | public class JSONStringer { 56 | private static final int maxdepth = 20; 57 | 58 | /** 59 | * The comma flag determines if a comma should be output before the next 60 | * value. 61 | */ 62 | private boolean comma; 63 | 64 | /** 65 | * The current mode. Values: 66 | * 'a' (array), 67 | * 'd' (done), 68 | * 'i' (initial), 69 | * 'k' (key), 70 | * 'o' (object). 71 | */ 72 | private char mode; 73 | 74 | /** 75 | * The string buffer that holds the JSON text that is built. 76 | */ 77 | private StringBuffer sb; 78 | 79 | /** 80 | * The object/array stack. 81 | */ 82 | private char stack[]; 83 | 84 | /** 85 | * The stack top index. A value of 0 indicates that the stack is empty. 86 | */ 87 | private int top; 88 | 89 | /** 90 | * Make a fresh JSONStringer. It can be used to build one JSON text. 91 | */ 92 | public JSONStringer() { 93 | this.sb = new StringBuffer(); 94 | this.stack = new char[maxdepth]; 95 | this.top = 0; 96 | this.mode = 'i'; 97 | this.comma = false; 98 | } 99 | 100 | /** 101 | * Append a value. 102 | * @param s A string value. 103 | * @return this 104 | * @throws JSONException If the value is out of sequence. 105 | */ 106 | private JSONStringer append(String s) 107 | throws JSONException { 108 | if (s == null) { 109 | throw new JSONException("Null pointer"); 110 | } 111 | if (this.mode == 'o' || this.mode == 'a') { 112 | if (this.comma && this.mode == 'a') { 113 | this.sb.append(','); 114 | } 115 | this.sb.append(s); 116 | if (this.mode == 'o') { 117 | this.mode = 'k'; 118 | } 119 | this.comma = true; 120 | return this; 121 | } 122 | throw new JSONException("Value out of sequence."); 123 | } 124 | 125 | /** 126 | * Begin appending a new array. All values until the balancing 127 | * endArray will be appended to this array. The 128 | * endArray method must be called to mark the array's end. 129 | * @return this 130 | * @throws JSONException If the nesting is too deep, or if the object is 131 | * started in the wrong place (for example as a key or after the end of the 132 | * outermost array or object). 133 | */ 134 | public JSONStringer array() throws JSONException { 135 | if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { 136 | push('a'); 137 | this.append("["); 138 | this.comma = false; 139 | return this; 140 | } 141 | throw new JSONException("Misplaced array."); 142 | } 143 | 144 | /** 145 | * End something. 146 | * @param m Mode 147 | * @param c Closing character 148 | * @return this 149 | * @throws JSONException If unbalanced. 150 | */ 151 | private JSONStringer end(char m, char c) throws JSONException { 152 | if (this.mode != m) { 153 | throw new JSONException(m == 'o' ? "Misplaced endObject." : 154 | "Misplaced endArray."); 155 | } 156 | pop(m); 157 | this.sb.append(c); 158 | this.comma = true; 159 | return this; 160 | } 161 | 162 | /** 163 | * End an array. This method most be called to balance calls to 164 | * array. 165 | * @return this 166 | * @throws JSONException If incorrectly nested. 167 | */ 168 | public JSONStringer endArray() throws JSONException { 169 | return end('a', ']'); 170 | } 171 | 172 | /** 173 | * End an object. This method most be called to balance calls to 174 | * object. 175 | * @return this 176 | * @throws JSONException If incorrectly nested. 177 | */ 178 | public JSONStringer endObject() throws JSONException { 179 | return end('k', '}'); 180 | } 181 | 182 | /** 183 | * Append a key. The key will be associated with the next value. In an 184 | * object, every value must be preceded by a key. 185 | * @param s A key string. 186 | * @return this 187 | * @throws JSONException If the key is out of place. For example, keys 188 | * do not belong in arrays or if the key is null. 189 | */ 190 | public JSONStringer key(String s) 191 | throws JSONException { 192 | if (s == null) { 193 | throw new JSONException("Null key."); 194 | } 195 | if (this.mode == 'k') { 196 | if (this.comma) { 197 | this.sb.append(','); 198 | } 199 | this.sb.append(JSONObject.quote(s)); 200 | this.sb.append(':'); 201 | this.comma = false; 202 | this.mode = 'o'; 203 | return this; 204 | } 205 | throw new JSONException("Misplaced key."); 206 | } 207 | 208 | 209 | /** 210 | * Begin appending a new object. All keys and values until the balancing 211 | * endObject will be appended to this object. The 212 | * endObject method must be called to mark the object's end. 213 | * @return this 214 | * @throws JSONException If the nesting is too deep, or if the object is 215 | * started in the wrong place (for example as a key or after the end of the 216 | * outermost array or object). 217 | */ 218 | public JSONStringer object() throws JSONException { 219 | if (this.mode == 'i') { 220 | this.mode = 'o'; 221 | } 222 | if (this.mode == 'o' || this.mode == 'a') { 223 | this.append("{"); 224 | push('k'); 225 | this.comma = false; 226 | return this; 227 | } 228 | throw new JSONException("Misplaced object."); 229 | 230 | } 231 | 232 | 233 | /** 234 | * Pop an array or object scope. 235 | * @param c The scope to close. 236 | * @throws JSONException If nesting is wrong. 237 | */ 238 | private void pop(char c) throws JSONException { 239 | if (this.top <= 0 || this.stack[this.top - 1] != c) { 240 | throw new JSONException("Nesting error."); 241 | } 242 | this.top -= 1; 243 | this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1]; 244 | } 245 | 246 | /** 247 | * Push an array or object scope. 248 | * @param c The scope to open. 249 | * @throws JSONException If nesting is too deep. 250 | */ 251 | private void push(char c) throws JSONException { 252 | if (this.top >= maxdepth) { 253 | throw new JSONException("Nesting too deep."); 254 | } 255 | this.stack[this.top] = c; 256 | this.mode = c; 257 | this.top += 1; 258 | } 259 | 260 | 261 | /** 262 | * Append either the value true or the value 263 | * false. 264 | * @param b A boolean. 265 | * @return this 266 | * @throws JSONException 267 | */ 268 | public JSONStringer value(boolean b) throws JSONException { 269 | return this.append(b ? "true" : "false"); 270 | } 271 | 272 | /** 273 | * Append a double value. 274 | * @param d A double. 275 | * @return this 276 | * @throws JSONException If the number is not finite. 277 | */ 278 | public JSONStringer value(double d) throws JSONException { 279 | return this.value(new Double(d)); 280 | } 281 | 282 | /** 283 | * Append a long value. 284 | * @param l A long. 285 | * @return this 286 | * @throws JSONException 287 | */ 288 | public JSONStringer value(long l) throws JSONException { 289 | return this.append(Long.toString(l)); 290 | } 291 | 292 | 293 | /** 294 | * Append an object value. 295 | * @param o The object to append. It can be null, or a Boolean, Number, 296 | * String, JSONObject, or JSONArray. 297 | * @return this 298 | * @throws JSONException If the value is out of sequence. 299 | */ 300 | public JSONStringer value(Object o) throws JSONException { 301 | if (JSONObject.NULL.equals(o)) { 302 | return this.append("null"); 303 | } 304 | if (o instanceof Number) { 305 | JSONObject.testValidity(o); 306 | return this.append(JSONObject.numberToString((Number)o)); 307 | } 308 | if (o instanceof Boolean || 309 | o instanceof JSONArray || o instanceof JSONObject) { 310 | return this.append(o.toString()); 311 | } 312 | return this.append(JSONObject.quote(o.toString())); 313 | } 314 | 315 | /** 316 | * Return the JSON text. This method is used to obtain the product of the 317 | * JSONStringer instance. It will return null if there was a 318 | * problem in the construction of the JSON text (such as the calls to 319 | * array were not properly balanced with calls to 320 | * endArray). 321 | * @return The JSON text. 322 | */ 323 | public String toString() { 324 | return this.mode == 'd' ? this.sb.toString() : null; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONTokener.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | 26 | /** 27 | * A JSONTokener takes a source string and extracts characters and tokens from 28 | * it. It is used by the JSONObject and JSONArray constructors to parse 29 | * JSON source strings. 30 | * @author JSON.org 31 | * @version 2 32 | */ 33 | public class JSONTokener { 34 | 35 | /** 36 | * The index of the next character. 37 | */ 38 | private int myIndex; 39 | 40 | 41 | /** 42 | * The source string being tokenized. 43 | */ 44 | private String mySource; 45 | 46 | 47 | /** 48 | * Construct a JSONTokener from a string. 49 | * 50 | * @param s A source string. 51 | */ 52 | public JSONTokener(String s) { 53 | this.myIndex = 0; 54 | this.mySource = s; 55 | } 56 | 57 | 58 | /** 59 | * Back up one character. This provides a sort of lookahead capability, 60 | * so that you can test for a digit or letter before attempting to parse 61 | * the next number or identifier. 62 | */ 63 | public void back() { 64 | if (this.myIndex > 0) { 65 | this.myIndex -= 1; 66 | } 67 | } 68 | 69 | 70 | 71 | /** 72 | * Get the hex value of a character (base16). 73 | * @param c A character between '0' and '9' or between 'A' and 'F' or 74 | * between 'a' and 'f'. 75 | * @return An int between 0 and 15, or -1 if c was not a hex digit. 76 | */ 77 | public static int dehexchar(char c) { 78 | if (c >= '0' && c <= '9') { 79 | return c - '0'; 80 | } 81 | if (c >= 'A' && c <= 'F') { 82 | return c - ('A' - 10); 83 | } 84 | if (c >= 'a' && c <= 'f') { 85 | return c - ('a' - 10); 86 | } 87 | return -1; 88 | } 89 | 90 | 91 | /** 92 | * Determine if the source string still contains characters that next() 93 | * can consume. 94 | * @return true if not yet at the end of the source. 95 | */ 96 | public boolean more() { 97 | return this.myIndex < this.mySource.length(); 98 | } 99 | 100 | 101 | /** 102 | * Get the next character in the source string. 103 | * 104 | * @return The next character, or 0 if past the end of the source string. 105 | */ 106 | public char next() { 107 | if (more()) { 108 | char c = this.mySource.charAt(this.myIndex); 109 | this.myIndex += 1; 110 | return c; 111 | } 112 | return 0; 113 | } 114 | 115 | 116 | /** 117 | * Consume the next character, and check that it matches a specified 118 | * character. 119 | * @param c The character to match. 120 | * @return The character. 121 | * @throws JSONException if the character does not match. 122 | */ 123 | public char next(char c) throws JSONException { 124 | char n = next(); 125 | if (n != c) { 126 | throw syntaxError("Expected '" + c + "' and instead saw '" + 127 | n + "'."); 128 | } 129 | return n; 130 | } 131 | 132 | 133 | /** 134 | * Get the next n characters. 135 | * 136 | * @param n The number of characters to take. 137 | * @return A string of n characters. 138 | * @throws JSONException 139 | * Substring bounds error if there are not 140 | * n characters remaining in the source string. 141 | */ 142 | public String next(int n) throws JSONException { 143 | int i = this.myIndex; 144 | int j = i + n; 145 | if (j >= this.mySource.length()) { 146 | throw syntaxError("Substring bounds error"); 147 | } 148 | this.myIndex += n; 149 | return this.mySource.substring(i, j); 150 | } 151 | 152 | 153 | /** 154 | * Get the next char in the string, skipping whitespace 155 | * and comments (slashslash, slashstar, and hash). 156 | * @throws JSONException 157 | * @return A character, or 0 if there are no more characters. 158 | */ 159 | public char nextClean() throws JSONException { 160 | for (;;) { 161 | char c = next(); 162 | if (c == '/') { 163 | switch (next()) { 164 | case '/': 165 | do { 166 | c = next(); 167 | } while (c != '\n' && c != '\r' && c != 0); 168 | break; 169 | case '*': 170 | for (;;) { 171 | c = next(); 172 | if (c == 0) { 173 | throw syntaxError("Unclosed comment."); 174 | } 175 | if (c == '*') { 176 | if (next() == '/') { 177 | break; 178 | } 179 | back(); 180 | } 181 | } 182 | break; 183 | default: 184 | back(); 185 | return '/'; 186 | } 187 | } else if (c == '#') { 188 | do { 189 | c = next(); 190 | } while (c != '\n' && c != '\r' && c != 0); 191 | } else if (c == 0 || c > ' ') { 192 | return c; 193 | } 194 | } 195 | } 196 | 197 | 198 | /** 199 | * Return the characters up to the next close quote character. 200 | * Backslash processing is done. The formal JSON format does not 201 | * allow strings in single quotes, but an implementation is allowed to 202 | * accept them. 203 | * @param quote The quoting character, either 204 | * " (double quote) or 205 | * ' (single quote). 206 | * @return A String. 207 | * @throws JSONException Unterminated string. 208 | */ 209 | public String nextString(char quote) throws JSONException { 210 | char c; 211 | StringBuffer sb = new StringBuffer(); 212 | for (;;) { 213 | c = next(); 214 | switch (c) { 215 | case 0: 216 | case '\n': 217 | case '\r': 218 | throw syntaxError("Unterminated string"); 219 | case '\\': 220 | c = next(); 221 | switch (c) { 222 | case 'b': 223 | sb.append('\b'); 224 | break; 225 | case 't': 226 | sb.append('\t'); 227 | break; 228 | case 'n': 229 | sb.append('\n'); 230 | break; 231 | case 'f': 232 | sb.append('\f'); 233 | break; 234 | case 'r': 235 | sb.append('\r'); 236 | break; 237 | case 'u': 238 | sb.append((char)Integer.parseInt(next(4), 16)); 239 | break; 240 | case 'x' : 241 | sb.append((char) Integer.parseInt(next(2), 16)); 242 | break; 243 | default: 244 | sb.append(c); 245 | } 246 | break; 247 | default: 248 | if (c == quote) { 249 | return sb.toString(); 250 | } 251 | sb.append(c); 252 | } 253 | } 254 | } 255 | 256 | 257 | /** 258 | * Get the text up but not including the specified character or the 259 | * end of line, whichever comes first. 260 | * @param d A delimiter character. 261 | * @return A string. 262 | */ 263 | public String nextTo(char d) { 264 | StringBuffer sb = new StringBuffer(); 265 | for (;;) { 266 | char c = next(); 267 | if (c == d || c == 0 || c == '\n' || c == '\r') { 268 | if (c != 0) { 269 | back(); 270 | } 271 | return sb.toString().trim(); 272 | } 273 | sb.append(c); 274 | } 275 | } 276 | 277 | 278 | /** 279 | * Get the text up but not including one of the specified delimeter 280 | * characters or the end of line, whichever comes first. 281 | * @param delimiters A set of delimiter characters. 282 | * @return A string, trimmed. 283 | */ 284 | public String nextTo(String delimiters) { 285 | char c; 286 | StringBuffer sb = new StringBuffer(); 287 | for (;;) { 288 | c = next(); 289 | if (delimiters.indexOf(c) >= 0 || c == 0 || 290 | c == '\n' || c == '\r') { 291 | if (c != 0) { 292 | back(); 293 | } 294 | return sb.toString().trim(); 295 | } 296 | sb.append(c); 297 | } 298 | } 299 | 300 | 301 | /** 302 | * Get the next value. The value can be a Boolean, Double, Integer, 303 | * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. 304 | * @throws JSONException If syntax error. 305 | * 306 | * @return An object. 307 | */ 308 | public Object nextValue() throws JSONException { 309 | char c = nextClean(); 310 | String s; 311 | 312 | switch (c) { 313 | case '"': 314 | case '\'': 315 | return nextString(c); 316 | case '{': 317 | back(); 318 | return new JSONObject(this); 319 | case '[': 320 | back(); 321 | return new JSONArray(this); 322 | } 323 | 324 | /* 325 | * Handle unquoted text. This could be the values true, false, or 326 | * null, or it can be a number. An implementation (such as this one) 327 | * is allowed to also accept non-standard forms. 328 | * 329 | * Accumulate characters until we reach the end of the text or a 330 | * formatting character. 331 | */ 332 | 333 | StringBuffer sb = new StringBuffer(); 334 | char b = c; 335 | while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { 336 | sb.append(c); 337 | c = next(); 338 | } 339 | back(); 340 | 341 | /* 342 | * If it is true, false, or null, return the proper value. 343 | */ 344 | 345 | s = sb.toString().trim(); 346 | if (s.equals("")) { 347 | throw syntaxError("Missing value."); 348 | } 349 | if (s.equalsIgnoreCase("true")) { 350 | return Boolean.TRUE; 351 | } 352 | if (s.equalsIgnoreCase("false")) { 353 | return Boolean.FALSE; 354 | } 355 | if (s.equalsIgnoreCase("null")) { 356 | return JSONObject.NULL; 357 | } 358 | 359 | /* 360 | * If it might be a number, try converting it. We support the 0- and 0x- 361 | * conventions. If a number cannot be produced, then the value will just 362 | * be a string. Note that the 0-, 0x-, plus, and implied string 363 | * conventions are non-standard. A JSON parser is free to accept 364 | * non-JSON forms as long as it accepts all correct JSON forms. 365 | */ 366 | 367 | if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') { 368 | if (b == '0') { 369 | if (s.length() > 2 && 370 | (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { 371 | try { 372 | return new Integer(Integer.parseInt(s.substring(2), 373 | 16)); 374 | } catch (Exception e) { 375 | /* Ignore the error */ 376 | } 377 | } else { 378 | try { 379 | return new Integer(Integer.parseInt(s, 8)); 380 | } catch (Exception e) { 381 | /* Ignore the error */ 382 | } 383 | } 384 | } 385 | try { 386 | return new Integer(s); 387 | } catch (Exception e) { 388 | try { 389 | return new Long(s); 390 | } catch (Exception f) { 391 | try { 392 | return new Double(s); 393 | } catch (Exception g) { 394 | return s; 395 | } 396 | } 397 | } 398 | } 399 | return s; 400 | } 401 | 402 | 403 | /** 404 | * Skip characters until the next character is the requested character. 405 | * If the requested character is not found, no characters are skipped. 406 | * @param to A character to skip to. 407 | * @return The requested character, or zero if the requested character 408 | * is not found. 409 | */ 410 | public char skipTo(char to) { 411 | char c; 412 | int index = this.myIndex; 413 | do { 414 | c = next(); 415 | if (c == 0) { 416 | this.myIndex = index; 417 | return c; 418 | } 419 | } while (c != to); 420 | back(); 421 | return c; 422 | } 423 | 424 | 425 | /** 426 | * Skip characters until past the requested string. 427 | * If it is not found, we are left at the end of the source. 428 | * @param to A string to skip past. 429 | */ 430 | public void skipPast(String to) { 431 | this.myIndex = this.mySource.indexOf(to, this.myIndex); 432 | if (this.myIndex < 0) { 433 | this.myIndex = this.mySource.length(); 434 | } else { 435 | this.myIndex += to.length(); 436 | } 437 | } 438 | 439 | 440 | /** 441 | * Make a JSONException to signal a syntax error. 442 | * 443 | * @param message The error message. 444 | * @return A JSONException object, suitable for throwing 445 | */ 446 | public JSONException syntaxError(String message) { 447 | return new JSONException(message + toString()); 448 | } 449 | 450 | 451 | /** 452 | * Make a printable string of this JSONTokener. 453 | * 454 | * @return " at character [this.myIndex] of [this.mySource]" 455 | */ 456 | public String toString() { 457 | return " at character " + this.myIndex + " of " + this.mySource; 458 | } 459 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONType.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | 26 | public enum JSONType { 27 | OBJECT, 28 | ARRAY, 29 | SCALAR 30 | } -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/json/JSONValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 JSON.org 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | The Software shall be used for Good, not Evil. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package jp.codic.plugins.intellij.json; 25 | 26 | public interface JSONValue { 27 | public JSONType getType(); 28 | } 29 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/util/Debouncer.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.util; 2 | 3 | import java.util.concurrent.Executors; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | import java.util.concurrent.ScheduledFuture; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class Debouncer { 9 | private ScheduledExecutorService scheduledExecutorService ; 10 | private long delay = 0; 11 | 12 | private ScheduledFuture future; 13 | 14 | public Debouncer(long delay) { 15 | this.delay = delay; 16 | scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 17 | } 18 | 19 | public void push(Runnable runnable) { 20 | if (future != null) { 21 | future.cancel(false); 22 | future = null; 23 | } 24 | future = scheduledExecutorService.schedule(runnable, delay, TimeUnit.MILLISECONDS); 25 | } 26 | 27 | public void showdown() { 28 | scheduledExecutorService.shutdownNow(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/util/DefaultKeyListener.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.util; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.awt.event.KeyListener; 5 | import java.awt.event.MouseEvent; 6 | import java.awt.event.MouseListener; 7 | 8 | public abstract class DefaultKeyListener implements KeyListener { 9 | @Override 10 | public void keyTyped(KeyEvent keyEvent) { 11 | 12 | } 13 | 14 | @Override 15 | public void keyPressed(KeyEvent keyEvent) { 16 | 17 | } 18 | 19 | @Override 20 | public void keyReleased(KeyEvent keyEvent) { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/jp/codic/plugins/intellij/util/DefaultMouseListener.java: -------------------------------------------------------------------------------- 1 | package jp.codic.plugins.intellij.util; 2 | 3 | import java.awt.event.MouseEvent; 4 | import java.awt.event.MouseListener; 5 | 6 | public abstract class DefaultMouseListener implements MouseListener { 7 | 8 | @Override 9 | public void mouseClicked(MouseEvent mouseEvent) { 10 | 11 | } 12 | 13 | @Override 14 | public void mousePressed(MouseEvent mouseEvent) { 15 | 16 | } 17 | 18 | @Override 19 | public void mouseReleased(MouseEvent mouseEvent) { 20 | 21 | } 22 | 23 | @Override 24 | public void mouseEntered(MouseEvent mouseEvent) { 25 | 26 | } 27 | 28 | @Override 29 | public void mouseExited(MouseEvent mouseEvent) { 30 | 31 | } 32 | 33 | } 34 | --------------------------------------------------------------------------------