diff --git a/src/main/java/com/shuzijun/leetcode/plugin/actions/toolbar/PickAction.java b/src/main/java/com/shuzijun/leetcode/plugin/actions/toolbar/PickAction.java index b2f34d64..3e6fcdce 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/actions/toolbar/PickAction.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/actions/toolbar/PickAction.java @@ -1,6 +1,7 @@ package com.shuzijun.leetcode.plugin.actions.toolbar; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.components.JBScrollPane; import com.shuzijun.leetcode.plugin.actions.AbstractAction; import com.shuzijun.leetcode.plugin.manager.ViewManager; @@ -21,6 +22,8 @@ public void actionPerformed(AnActionEvent anActionEvent, Config config) { return; } JBScrollPane scrollPane = WindowFactory.getDataContext(anActionEvent.getProject()).getData(DataKeys.LEETCODE_PROJECTS_SCROLL); - ViewManager.pick(tree, scrollPane); + ApplicationManager.getApplication().invokeAndWait(() -> { + ViewManager.pick(tree, scrollPane); + }); } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/listener/ColorListener.java b/src/main/java/com/shuzijun/leetcode/plugin/listener/ColorListener.java new file mode 100644 index 00000000..97deeca9 --- /dev/null +++ b/src/main/java/com/shuzijun/leetcode/plugin/listener/ColorListener.java @@ -0,0 +1,31 @@ +package com.shuzijun.leetcode.plugin.listener; + +import com.intellij.ui.ColorPicker; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/** + * @author shuzijun + */ +public class ColorListener extends MouseAdapter { + private JLabel label; + private JPanel mainPanel; + + public ColorListener(JPanel mainPanel,JLabel label) { + this.mainPanel = mainPanel; + this.label = label; + } + + @Override + public void mouseClicked(MouseEvent e) { + Color newColor = ColorPicker.showDialog(mainPanel, label.getText()+" Color", label.getForeground(), true, null, true); + if(newColor!=null){ + label.setForeground(newColor); + } + } + + +} diff --git a/src/main/java/com/shuzijun/leetcode/plugin/manager/CodeManager.java b/src/main/java/com/shuzijun/leetcode/plugin/manager/CodeManager.java index 13131d85..af9c2b4a 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/manager/CodeManager.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/manager/CodeManager.java @@ -6,6 +6,9 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -14,19 +17,16 @@ import com.shuzijun.leetcode.plugin.setting.ProjectConfig; import com.shuzijun.leetcode.plugin.utils.*; import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.math.BigDecimal; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * @author shuzijun */ public class CodeManager { - private static ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); - public static void openCode(Question question, Project project) { Config config = PersistentConfig.getInstance().getInitConfig(); String codeType = config.getCodeType(); @@ -173,7 +173,7 @@ public static void SubmitCode(Question question, Project project) { if (response != null && response.getStatusCode() == 200) { String body = response.getBody(); JSONObject returnObj = JSONObject.parseObject(body); - cachedThreadPool.execute(new SubmitCheckTask(returnObj, codeTypeEnum, question, project)); + ProgressManager.getInstance().run(new SubmitCheckTask(returnObj, codeTypeEnum, question, project)); MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("request.pending")); } else if (response != null && response.getStatusCode() == 429) { MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("request.pending")); @@ -218,7 +218,7 @@ public static void RunCodeCode(Question question, Project project) { String body = response.getBody(); JSONObject returnObj = JSONObject.parseObject(body); - cachedThreadPool.execute(new RunCodeCheckTask(returnObj, project)); + ProgressManager.getInstance().run(new RunCodeCheckTask(returnObj, project)); MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("request.pending")); } else { LogUtils.LOG.error("RuncodeCode failure " + response.getBody()); @@ -336,7 +336,7 @@ private static String getContent(JSONObject jsonObject) { return sb.toString(); } - private static class SubmitCheckTask implements Runnable { + private static class SubmitCheckTask extends Task.Backgroundable { private Question question; private JSONObject returnObj; @@ -344,6 +344,7 @@ private static class SubmitCheckTask implements Runnable { private Project project; public SubmitCheckTask(JSONObject returnObj, CodeTypeEnum codeTypeEnum, Question question, Project project) { + super(project,"leetcode.editor.submitCheckTask",true); this.returnObj = returnObj; this.codeTypeEnum = codeTypeEnum; this.question = question; @@ -351,9 +352,13 @@ public SubmitCheckTask(JSONObject returnObj, CodeTypeEnum codeTypeEnum, Question } @Override - public void run() { + public void run(@NotNull ProgressIndicator progressIndicator) { String key = returnObj.getString("submission_id"); for (int i = 0; i < 50; i++) { + if(progressIndicator.isCanceled()){ + MessageUtils.getInstance(project).showWarnMsg("error", PropertiesUtils.getInfo("request.cancel")); + return; + } try { HttpRequest httpRequest = HttpRequest.get(URLUtils.getLeetcodeSubmissions() + key + "/check/"); HttpResponse response = HttpRequestUtils.executeGet(httpRequest); @@ -424,22 +429,27 @@ private static String buildErrorMsg(JSONObject errorBody) { } - private static class RunCodeCheckTask implements Runnable { + private static class RunCodeCheckTask extends Task.Backgroundable { private JSONObject returnObj; private Project project; public RunCodeCheckTask(JSONObject returnObj, Project project) { + super(project,"leetcode.editor.runCodeCheckTask",true); this.returnObj = returnObj; this.project = project; } @Override - public void run() { + public void run(@NotNull ProgressIndicator progressIndicator) { String key = returnObj.getString("interpret_expected_id"); if (StringUtils.isBlank(key)) { key = returnObj.getString("interpret_id"); } for (int i = 0; i < 50; i++) { + if(progressIndicator.isCanceled()){ + MessageUtils.getInstance(project).showWarnMsg("error", PropertiesUtils.getInfo("request.cancel")); + return; + } String body = null; try { HttpRequest httpRequest = HttpRequest.get(URLUtils.getLeetcodeSubmissions() + key + "/check/"); diff --git a/src/main/java/com/shuzijun/leetcode/plugin/manager/ViewManager.java b/src/main/java/com/shuzijun/leetcode/plugin/manager/ViewManager.java index 3b502492..9bf11da6 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/manager/ViewManager.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/manager/ViewManager.java @@ -184,8 +184,11 @@ public static void pick(JTree tree, JBScrollPane scrollPane) { } int i = (int) (Math.random() * node.getChildCount()); DefaultMutableTreeNode select = (DefaultMutableTreeNode) node.getChildAt(i); - tree.setSelectionPath(new TreePath(select.getPath())); - Point point = new Point(0, i < 3 ? 0 : (i - 3) * tree.getRowHeight()); + + TreePath toShowPath = new TreePath(select.getPath()); + tree.setSelectionPath(toShowPath); + Rectangle bounds = tree.getPathBounds(toShowPath); + Point point = new Point(0, (int) bounds.getY()); JViewport viewport = scrollPane.getViewport(); viewport.setViewPosition(point); return; diff --git a/src/main/java/com/shuzijun/leetcode/plugin/model/Config.java b/src/main/java/com/shuzijun/leetcode/plugin/model/Config.java index 8147eece..a6297270 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/model/Config.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/model/Config.java @@ -2,6 +2,7 @@ import com.intellij.util.xmlb.annotations.Transient; +import java.awt.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -82,7 +83,7 @@ public class Config { /** * 题目颜色 */ - private String levelColour = "#5CB85C;#F0AD4E;#D9534F"; + private String levelColour = Constant.LEVEL_COLOUR; private List favoriteList; @@ -232,8 +233,57 @@ public String getCookie(String user) { public String getLevelColour() { return levelColour; } + @Transient + public Color[] getFormatLevelColour() { + Color[] formatColors = new Color[3]; + formatColors[0] = new Color(92, 184, 92); + formatColors[1] = new Color(240, 173, 78); + formatColors[2] = new Color(217, 83, 79); + String[] colors = getLevelColour().split(";"); + if (colors.length > 0) { + try { + formatColors[0] = new Color(Integer.parseInt(colors[0].replace("#", ""), 16)); + } catch (Exception ignore) { + } + } + if (colors.length > 1) { + try { + formatColors[1] = new Color(Integer.parseInt(colors[1].replace("#", ""), 16)); + } catch (Exception ignore) { + } + } + if (colors.length > 2) { + try { + formatColors[2] = new Color(Integer.parseInt(colors[2].replace("#", ""), 16)); + } catch (Exception ignore) { + } + } + return formatColors; + } public void setLevelColour(String levelColour) { + if(levelColour ==null || levelColour.isEmpty()){ + this.levelColour = Constant.LEVEL_COLOUR; + }else { + this.levelColour = levelColour; + } + } + + @Transient + public void setFormatLevelColour(Color... colors) { + String levelColour = ""; + if (colors != null && colors.length > 0) { + for (Color color : colors) { + String R = Integer.toHexString(color.getRed()); + R = R.length() < 2 ? ('0' + R) : R; + String G = Integer.toHexString(color.getGreen()); + G = G.length() < 2 ? ('0' + G) : G; + String B = Integer.toHexString(color.getBlue()); + B = B.length() < 2 ? ('0' + B) : B; + + levelColour = levelColour + '#' + R + G + B + ";"; + } + } this.levelColour = levelColour; } @@ -244,4 +294,28 @@ public Boolean getEnglishContent() { public void setEnglishContent(Boolean englishContent) { this.englishContent = englishContent; } + + + public boolean isModified(Config config){ + if(config ==null){ + return false; + } + if (version != null ? !version.equals(config.version) : config.version != null) return false; + if (loginName != null ? !loginName.equals(config.loginName) : config.loginName != null) return false; + if (filePath != null ? !filePath.equals(config.filePath) : config.filePath != null) return false; + if (codeType != null ? !codeType.equals(config.codeType) : config.codeType != null) return false; + if (url != null ? !url.equals(config.url) : config.url != null) return false; + if (update != null ? !update.equals(config.update) : config.update != null) return false; + if (proxy != null ? !proxy.equals(config.proxy) : config.proxy != null) return false; + if (customCode != null ? !customCode.equals(config.customCode) : config.customCode != null) return false; + if (englishContent != null ? !englishContent.equals(config.englishContent) : config.englishContent != null) + return false; + if (customFileName != null ? !customFileName.equals(config.customFileName) : config.customFileName != null) + return false; + if (customTemplate != null ? !customTemplate.equals(config.customTemplate) : config.customTemplate != null) + return false; + return levelColour != null ? levelColour.equals(config.levelColour) : config.levelColour == null; + } + + } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/model/Constant.java b/src/main/java/com/shuzijun/leetcode/plugin/model/Constant.java index 1bb4042d..5c885fb7 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/model/Constant.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/model/Constant.java @@ -72,4 +72,9 @@ public class Constant { public static final Integer PLUGIN_CONFIG_VERSION_1 = 1; //第二版本,不兼容之前的临时目录,从此版本开始更换新临时目录 public static final Integer PLUGIN_CONFIG_VERSION_2 = 2; + + /** + * 默认题目颜色 + */ + public static final String LEVEL_COLOUR = "#5CB85C;#F0AD4E;#D9534F"; } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/renderer/CustomTreeCellRenderer.java b/src/main/java/com/shuzijun/leetcode/plugin/renderer/CustomTreeCellRenderer.java index 06cc8d36..ecd4203e 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/renderer/CustomTreeCellRenderer.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/renderer/CustomTreeCellRenderer.java @@ -5,7 +5,6 @@ import com.shuzijun.leetcode.plugin.model.Config; import com.shuzijun.leetcode.plugin.model.Question; import com.shuzijun.leetcode.plugin.setting.PersistentConfig; -import org.apache.commons.lang.StringUtils; import javax.imageio.ImageIO; import javax.swing.*; @@ -19,36 +18,22 @@ */ public class CustomTreeCellRenderer extends NodeRenderer { - private Color Level1 = new Color(92, 184, 92); - private Color Level2 = new Color(240, 173, 78); - private Color Level3 = new Color(217, 83, 79); + private static Color Level1 = new Color(92, 184, 92); + private static Color Level2 = new Color(240, 173, 78); + private static Color Level3 = new Color(217, 83, 79); public CustomTreeCellRenderer() { + loaColor(); + } + + public static void loaColor(){ Config config = PersistentConfig.getInstance().getInitConfig(); if (config != null) { - if (StringUtils.isNotBlank(config.getLevelColour())) { - String[] colors = config.getLevelColour().split(";"); - if (colors.length > 0) { - try { - Level1 = new Color(Integer.parseInt(colors[0].replace("#",""), 16)); - } catch (Exception ignore) { - } - } - if (colors.length > 1) { - try { - Level2 = new Color(Integer.parseInt(colors[1].replace("#",""), 16)); - } catch (Exception ignore) { - } - } - if (colors.length > 2) { - try { - Level3 = new Color(Integer.parseInt(colors[2].replace("#",""), 16)); - } catch (Exception ignore) { - } - } - } + Color[] colors = config.getFormatLevelColour(); + Level1 = colors[0]; + Level2 = colors[1]; + Level3 = colors[2]; } - } public static BufferedImage getResourceBufferedImage(String filePath) { @@ -71,7 +56,6 @@ public void customizeCellRenderer(JTree tree, Object value, boolean selected, bo DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; Question question = (Question) node.getUserObject(); - if (question.getLevel() == null) { } else if (question.getLevel() == 1) { diff --git a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingConfigurable.java b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingConfigurable.java index 2565a151..b07bb90e 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingConfigurable.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingConfigurable.java @@ -45,7 +45,6 @@ public Runnable enableSearch(String option) { @Override public JComponent createComponent() { mainPanel = new SettingUI(); - mainPanel.createUI(); return mainPanel.getContentPane(); } @@ -69,4 +68,5 @@ public void disposeUIResources() { mainPanel.disposeUIResources(); mainPanel = null; } + } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.form b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.form new file mode 100644 index 00000000..da424046 --- /dev/null +++ b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.form @@ -0,0 +1,351 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java index dd75b1ff..7a6e8248 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java @@ -11,14 +11,16 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.ui.TextBrowseFolderListener; import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBPasswordField; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBTextField; +import com.shuzijun.leetcode.plugin.listener.ColorListener; import com.shuzijun.leetcode.plugin.listener.DonateListener; import com.shuzijun.leetcode.plugin.model.CodeTypeEnum; import com.shuzijun.leetcode.plugin.model.Config; import com.shuzijun.leetcode.plugin.model.Constant; +import com.shuzijun.leetcode.plugin.renderer.CustomTreeCellRenderer; +import com.shuzijun.leetcode.plugin.timer.TimerBarWidget; import com.shuzijun.leetcode.plugin.utils.MTAUtils; import com.shuzijun.leetcode.plugin.utils.PropertiesUtils; import com.shuzijun.leetcode.plugin.utils.URLUtils; @@ -33,152 +35,117 @@ /** * @author shuzijun */ -public class SettingUI extends JDialog { +public class SettingUI { + private JPanel mainPanel; + private JComboBox webComboBox; + private JComboBox codeComboBox; + private JBTextField userNameField; + private JBPasswordField passwordField; + private JLabel easyLabel; + private JLabel mediumLabel; + private JLabel hardLabel; + private TextFieldWithBrowseButton fileFolderBtn; + private JTextField JCEFFileField; + private JCheckBox customCodeBox; + private JCheckBox updateCheckBox; + private JCheckBox proxyCheckBox; + private JCheckBox englishContentBox; + + private JLabel templateConfigHelp; + private JPanel codeFileName; + private JPanel codeTemplate; + private JPanel templateConstant; - public JPanel mainPanel = new JBPanel(); - - private JTextField userNameField = new JBTextField(10); - private JPasswordField passwordField = new JBPasswordField(); - private TextFieldWithBrowseButton fileFolderBtn = new TextFieldWithBrowseButton(); - private JTextField LevelColourField = new JBTextField(20); - - private JComboBox webComboBox = new JComboBox(); - private JComboBox codeComboBox = new JComboBox(); - private JCheckBox customCodeBox = new JCheckBox("Custom code template"); - private JCheckBox updateCheckBox = new JCheckBox("Check plugin update"); - private JCheckBox proxyCheckBox = new JCheckBox("proxy(HTTP Proxy)"); - private JCheckBox englishContentBox = new JCheckBox("English Content"); private Editor fileNameEditor = null; private Editor templateEditor = null; private Editor templateHelpEditor = null; + public SettingUI() { - setContentPane(mainPanel); + initUI(); } - public void createUI() { - - GridBagLayout giGridBagLayout = new GridBagLayout(); - GridBagConstraints constraints = new GridBagConstraints(); - - mainPanel.setLayout(giGridBagLayout); - - constraints.fill = GridBagConstraints.HORIZONTAL; - constraints.insets = new Insets(5, 5, 5, 5); - - addComponent(new JLabel("URL:"), constraints, 0, 0, 0, 0); + public void initUI() { webComboBox.addItem(URLUtils.leetcodecn); webComboBox.addItem(URLUtils.leetcode); - webComboBox.setSelectedIndex(0); - addComponent(webComboBox, constraints, 1, 0, 2, 0); - - addComponent(new JLabel("Code Type:"), constraints, 3, 0, 3, 0); for (CodeTypeEnum c : CodeTypeEnum.values()) { codeComboBox.addItem(c.getType()); } - codeComboBox.setSelectedIndex(0); - addComponent(codeComboBox, constraints, 4, 0, 5, 0); - - updateCheckBox.setSelected(true); - addComponent(updateCheckBox, constraints, 6, 0, 6, 0); - - - addComponent(new JLabel("LoginName:"), constraints, 0, 1, 0, 1); - - addComponent(userNameField, constraints, 1, 1, 2, 1); - - addComponent(new JLabel("Password:"), constraints, 3, 1, 3, 1); - - addComponent(passwordField, constraints, 4, 1, 5, 1); - - proxyCheckBox.setSelected(false); - proxyCheckBox.setToolTipText("Employ File | Settings | Appearance & Behavior | System Settings | HTTP Proxy"); - proxyCheckBox.setEnabled(false); - addComponent(proxyCheckBox, constraints, 6, 1, 7, 1); + easyLabel.addMouseListener(new ColorListener(mainPanel, easyLabel)); + mediumLabel.addMouseListener(new ColorListener(mainPanel, mediumLabel)); + hardLabel.addMouseListener(new ColorListener(mainPanel, hardLabel)); - addComponent(new JLabel("TempFilePath:"), constraints, 0, 2, 0, 2); - - //fileFolderBtn.setTextFieldPreferredWidth(45); - fileFolderBtn.setText(System.getProperty("java.io.tmpdir")); fileFolderBtn.addBrowseFolderListener(new TextBrowseFolderListener(FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor()) { }); - addComponent(fileFolderBtn, constraints, 1, 2, 5, 2); - - customCodeBox.setSelected(false); - addComponent(customCodeBox, constraints, 6, 2, 7, 2); - - addComponent(new JLabel("JCEFFilePath:"), constraints, 0, 3, 0, 3); - addComponent(new JLabel(PathManager.getPluginsPath() + File.separator + "leetcode-editor" + File.separator + "natives" + File.separator), constraints, 1, 3, 5, 3); - - addComponent(new JLabel("LevelColour:"), constraints, 0, 4, 0, 4); - addComponent(LevelColourField, constraints, 1, 4, 5, 4); + JCEFFileField.setText(PathManager.getPluginsPath() + File.separator + "leetcode-editor" + File.separator + "natives" + File.separator); - englishContentBox.setSelected(false); - addComponent(englishContentBox, constraints, 6, 3, 7, 3); + customCodeBox.addActionListener(new DonateListener(customCodeBox)); - JLabel templateConfigHelp = new JLabel("CustomConfig(help)"); templateConfigHelp.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { BrowserUtil.browse("https://github.com/shuzijun/leetcode-editor/blob/master/doc/CustomCode.md"); } }); - templateConfigHelp.setForeground(new Color(88, 157, 246)); - addComponent(templateConfigHelp, constraints, 0, 5, 0, 5); - addComponent(new JSeparator(), constraints, 1, 5, 8, 5); - - addComponent(new JLabel("CodeFileName:"), constraints, 0, 6, 0, 6); fileNameEditor = EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""), null, FileTypeManager.getInstance().getFileTypeByExtension("vm"), false); EditorSettings settings = fileNameEditor.getSettings(); ((EditorImpl) fileNameEditor).setOneLineMode(true); + //额外的行 + settings.setAdditionalLinesCount(0); + //额外的列 + settings.setAdditionalColumnsCount(0); + settings.setCaretRowShown(false); + //折叠大纲 + settings.setFoldingOutlineShown(false); + //缩进 + settings.setIndentGuidesShown(false); + //线性标记区域 + settings.setLineMarkerAreaShown(false); + //行号 + settings.setLineNumbersShown(false); + //虚拟空间 + settings.setVirtualSpace(false); + //允许单逻辑行折叠 + settings.setAllowSingleLogicalLineFolding(false); + //滚动 + settings.setAnimatedScrolling(false); + //底部附加 + settings.setAdditionalPageAtBottom(false); + //代码自动折叠 + settings.setAutoCodeFoldingEnabled(false); + codeFileName.add(fileNameEditor.getComponent(), BorderLayout.CENTER); - settings.setAdditionalLinesCount(0); //额外的行 - settings.setAdditionalColumnsCount(0); //额外的列 - settings.setCaretRowShown(false); // - settings.setFoldingOutlineShown(false); //折叠大纲 - settings.setIndentGuidesShown(false); //缩进 - settings.setLineMarkerAreaShown(false); //线性标记区域 - settings.setLineNumbersShown(false); //行号 - settings.setVirtualSpace(false); //虚拟空间 - settings.setAllowSingleLogicalLineFolding(false);//允许单逻辑行折叠 - settings.setAnimatedScrolling(false); //滚动 - settings.setAdditionalPageAtBottom(false); //底部附加 - settings.setAutoCodeFoldingEnabled(false); //代码自动折叠 - - addComponent(fileNameEditor.getComponent(), constraints, 1, 6, 8, 6); - - constraints.anchor = GridBagConstraints.NORTHWEST; - addComponent(new JLabel("CodeTemplate:"), constraints, 0, 7, 0, 7); templateEditor = EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""), null, FileTypeManager.getInstance().getFileTypeByExtension("vm"), false); EditorSettings templateEditorSettings = templateEditor.getSettings(); - templateEditorSettings.setAdditionalLinesCount(0); //额外的行 - templateEditorSettings.setAdditionalColumnsCount(0); //额外的列 - templateEditorSettings.setLineMarkerAreaShown(false); //线性标记区域 - templateEditorSettings.setVirtualSpace(false); //虚拟空间 + templateEditorSettings.setAdditionalLinesCount(0); + templateEditorSettings.setAdditionalColumnsCount(0); + templateEditorSettings.setLineMarkerAreaShown(false); + templateEditorSettings.setVirtualSpace(false); JBScrollPane jbScrollPane = new JBScrollPane(templateEditor.getComponent()); - jbScrollPane.setMaximumSize(new Dimension(150, 50)); - addComponent(jbScrollPane, constraints, 1, 7, 8, 7); - - - addComponent(new JLabel("TemplateConstant:"), constraints, 0, 8, 0, 8); + codeTemplate.add(jbScrollPane, BorderLayout.CENTER); templateHelpEditor = EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(PropertiesUtils.getInfo("template.variable", "{", "}")), null, FileTypeManager.getInstance().getFileTypeByExtension("vm"), true); - EditorSettings templateHelpEditorSettings = templateHelpEditor.getSettings(); - templateHelpEditorSettings.setAdditionalLinesCount(0); //额外的行 - templateHelpEditorSettings.setAdditionalColumnsCount(0); //额外的列 - templateHelpEditorSettings.setLineMarkerAreaShown(false); //线性标记区域 - templateHelpEditorSettings.setLineNumbersShown(false); //行号 - templateHelpEditorSettings.setVirtualSpace(false); //虚拟空间 - - addComponent(templateHelpEditor.getComponent(), constraints, 1, 8, 8, 8); + templateHelpEditorSettings.setAdditionalLinesCount(0); + templateHelpEditorSettings.setAdditionalColumnsCount(0); + templateHelpEditorSettings.setLineMarkerAreaShown(false); + templateHelpEditorSettings.setLineNumbersShown(false); + templateHelpEditorSettings.setVirtualSpace(false); + templateConstant.add(templateHelpEditor.getComponent(), BorderLayout.CENTER); + + loadSetting(); + } + private void loadSetting() { + webComboBox.setSelectedIndex(0); + codeComboBox.setSelectedIndex(0); + fileFolderBtn.setText(System.getProperty("java.io.tmpdir")); Config config = PersistentConfig.getInstance().getInitConfig(); if (config != null) { @@ -194,37 +161,53 @@ public void mouseClicked(MouseEvent e) { webComboBox.setSelectedItem(config.getUrl()); } updateCheckBox.setSelected(config.getUpdate()); - //proxyCheckBox.setSelected(config.getProxy()); customCodeBox.setSelected(config.getCustomCode()); ApplicationManager.getApplication().runWriteAction(() -> { fileNameEditor.getDocument().setText(config.getCustomFileName()); templateEditor.getDocument().setText(config.getCustomTemplate()); }); - LevelColourField.setText(config.getLevelColour()); englishContentBox.setSelected(config.getEnglishContent()); + + Color[] colors = config.getFormatLevelColour(); + easyLabel.setForeground(colors[0]); + mediumLabel.setForeground(colors[1]); + hardLabel.setForeground(colors[2]); + } else { - LevelColourField.setText(new Config().getLevelColour()); + Color[] colors = new Config().getFormatLevelColour(); + easyLabel.setForeground(colors[0]); + mediumLabel.setForeground(colors[1]); + hardLabel.setForeground(colors[2]); ApplicationManager.getApplication().runWriteAction(() -> { fileNameEditor.getDocument().setText(Constant.CUSTOM_FILE_NAME); templateEditor.getDocument().setText(Constant.CUSTOM_TEMPLATE); }); } - customCodeBox.addActionListener(new DonateListener(customCodeBox)); + + } - private void addComponent(Component component, GridBagConstraints constraints, int x, int y, int ex, int ey) { - constraints.gridx = x; - constraints.gridy = y; - constraints.gridwidth = ex - x + 1; - constraints.gridheight = ey - y + 1; - constraints.weightx = (ex - x + 1) * 0.1; - constraints.weighty = (ey - y + 1) * 0.1; - mainPanel.add(component, constraints); + public JPanel getContentPane() { + return mainPanel; } public boolean isModified() { - boolean modified = true; - return modified; + Config config = PersistentConfig.getInstance().getInitConfig(); + if (config == null) { + return true; + } else { + Config currentState = new Config(); + process(currentState); + if (currentState.isModified(config)) { + if (passwordField.getText() != null && passwordField.getText().equals(PersistentConfig.getInstance().getPassword())) { + return false; + } else { + return true; + } + } else { + return true; + } + } } public void apply() { @@ -233,33 +216,34 @@ public void apply() { config = new Config(); config.setId(MTAUtils.getI("")); } + process(config); + File file = new File(config.getFilePath() + File.separator + PersistentConfig.PATH + File.separator); + if (!file.exists()) { + file.mkdirs(); + } + PersistentConfig.getInstance().setInitConfig(config); + PersistentConfig.getInstance().savePassword(passwordField.getText()); + CustomTreeCellRenderer.loaColor(); + TimerBarWidget.loaColor(); + } + + public void process(Config config) { config.setVersion(Constant.PLUGIN_CONFIG_VERSION_2); config.setLoginName(userNameField.getText()); config.setFilePath(fileFolderBtn.getText()); config.setCodeType(codeComboBox.getSelectedItem().toString()); config.setUrl(webComboBox.getSelectedItem().toString()); config.setUpdate(updateCheckBox.isSelected()); - //config.setProxy(proxyCheckBox.isSelected()); config.setCustomCode(customCodeBox.isSelected()); config.setCustomFileName(fileNameEditor.getDocument().getText()); config.setCustomTemplate(templateEditor.getDocument().getText()); - config.setLevelColour(LevelColourField.getText()); + config.setFormatLevelColour(easyLabel.getForeground(), mediumLabel.getForeground(), hardLabel.getForeground()); config.setEnglishContent(englishContentBox.isSelected()); - File file = new File(config.getFilePath() + File.separator + PersistentConfig.PATH + File.separator); - if (!file.exists()) { - file.mkdirs(); - } - PersistentConfig.getInstance().setInitConfig(config); - PersistentConfig.getInstance().savePassword(passwordField.getText()); } - public void reset() { - - } - @Override - public JPanel getContentPane() { - return mainPanel; + public void reset() { + loadSetting(); } public void disposeUIResources() { @@ -277,5 +261,4 @@ public void disposeUIResources() { } } - } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java b/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java index e7021d7e..4e390a05 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java @@ -29,12 +29,22 @@ public class TimerBarWidget implements CustomStatusBarWidget { private Project project; - private Color Level1 = new Color(92, 184, 92); - private Color Level2 = new Color(240, 173, 78); - private Color Level3 = new Color(217, 83, 79); + private static Color Level1 = new Color(92, 184, 92); + private static Color Level2 = new Color(240, 173, 78); + private static Color Level3 = new Color(217, 83, 79); public TimerBarWidget(Project project) { this.project = project; + loaColor(); + } + public static void loaColor(){ + Config config = PersistentConfig.getInstance().getInitConfig(); + if (config != null) { + Color[] colors = config.getFormatLevelColour(); + Level1 = colors[0]; + Level2 = colors[1]; + Level3 = colors[2]; + } } private JLabel label = new JLabel(time()); diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java index 6b44f450..3f50422d 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java @@ -1,6 +1,6 @@ package com.shuzijun.leetcode.plugin.utils; -import com.intellij.util.io.HttpRequests; +import com.shuzijun.leetcode.plugin.utils.io.HttpRequests; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHeaders; import org.jetbrains.annotations.NotNull; @@ -15,7 +15,7 @@ */ public class HttpRequestUtils { - private static CookieManager cookieManager = new CookieManager(); + private static final CookieManager cookieManager = new CookieManager(); static { CookieHandler.setDefault(cookieManager); @@ -23,32 +23,32 @@ public class HttpRequestUtils { public static HttpResponse executeGet(HttpRequest httpRequest) { + HttpResponse httpResponse = new HttpResponse(); try { - return HttpRequests.request(httpRequest.getUrl()) + HttpRequests.request(httpRequest.getUrl()) .throwStatusCodeException(false) .tuner(new HttpRequestTuner(httpRequest)) - .connect(new HttpResponseProcessor(httpRequest)); + .connect(new HttpResponseProcessor(httpRequest, httpResponse)); + } catch (IOException e) { LogUtils.LOG.error("HttpRequestUtils request error:", e); - HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(-1); - return httpResponse; } + return httpResponse; } public static HttpResponse executePost(HttpRequest httpRequest) { - + HttpResponse httpResponse = new HttpResponse(); try { - return HttpRequests.post(httpRequest.getUrl(), httpRequest.getContentType()) + HttpRequests.post(httpRequest.getUrl(), httpRequest.getContentType()) .throwStatusCodeException(false) .tuner(new HttpRequestTuner(httpRequest)) - .connect(new HttpResponseProcessor(httpRequest)); + .connect(new HttpResponseProcessor(httpRequest, httpResponse)); } catch (IOException e) { LogUtils.LOG.error("HttpRequestUtils request error:", e); - HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(-1); - return httpResponse; } + return httpResponse; } public static String getToken() { @@ -67,9 +67,6 @@ public static String getToken() { public static boolean isLogin() { HttpRequest request = HttpRequest.get(URLUtils.getLeetcodePoints()); HttpResponse response = executeGet(request); - if (response == null) { - return Boolean.FALSE; - } if (response.getStatusCode() == 200) { return Boolean.TRUE; } @@ -88,6 +85,18 @@ public static void resetHttpclient() { cookieManager.getCookieStore().removeAll(); } + + private static void defaultHeader(HttpRequest httpRequest) { + Map header = httpRequest.getHeader(); + header.putIfAbsent(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"); + header.putIfAbsent(HttpHeaders.ACCEPT, "*/*"); + //header.putIfAbsent(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br"); + header.putIfAbsent(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9"); + header.putIfAbsent("origin", URLUtils.getLeetcodeUrl()); + //header.putIfAbsent(":authority", URLUtils.getLeetcodeHost()); + //header.putIfAbsent(":scheme", "https"); + } + private static class HttpRequestTuner implements HttpRequests.ConnectionTuner { private HttpRequest httpRequest; @@ -108,48 +117,39 @@ public void tune(@NotNull URLConnection urlConnection) throws IOException { defaultHeader(httpRequest); httpRequest.getHeader().forEach((k, v) -> urlConnection.addRequestProperty(k, v)); } - - private static void defaultHeader(HttpRequest httpRequest) { - Map header = httpRequest.getHeader(); - header.putIfAbsent(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"); - header.putIfAbsent(HttpHeaders.ACCEPT, "*/*"); - //header.putIfAbsent(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br"); - header.putIfAbsent(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9"); - header.putIfAbsent("origin", URLUtils.getLeetcodeUrl()); - //header.putIfAbsent(":authority", URLUtils.getLeetcodeHost()); - //header.putIfAbsent(":scheme", "https"); - } - } private static class HttpResponseProcessor implements HttpRequests.RequestProcessor { - private HttpRequest httpRequest; + private final HttpRequest httpRequest; + private final HttpResponse httpResponse; - public HttpResponseProcessor(HttpRequest httpRequest) { + public HttpResponseProcessor(HttpRequest httpRequest, HttpResponse httpResponse) { this.httpRequest = httpRequest; + this.httpResponse = httpResponse; } @Override public HttpResponse process(@NotNull HttpRequests.Request request) throws IOException { + if (StringUtils.isNoneBlank(httpRequest.getBody())) { request.write(httpRequest.getBody()); } - HttpResponse httpResponse = new HttpResponse(); URLConnection urlConnection = request.getConnection(); if (!(urlConnection instanceof HttpURLConnection)) { httpResponse.setStatusCode(-1); + return httpResponse; } else { httpResponse.setStatusCode(((HttpURLConnection) urlConnection).getResponseCode()); } + try { httpResponse.setBody(request.readString()); } catch (IOException ignore) { } - return httpResponse; } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java index ecea314b..a4906516 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java @@ -1,14 +1,12 @@ package com.shuzijun.leetcode.plugin.utils; +import com.intellij.ide.plugins.PluginManager; +import com.intellij.openapi.extensions.PluginId; +import com.intellij.openapi.util.SystemInfo; import com.shuzijun.leetcode.plugin.model.Config; -import org.apache.http.HttpResponse; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpGet; +import com.shuzijun.leetcode.plugin.model.Constant; +import com.shuzijun.leetcode.plugin.utils.io.HttpRequests; import org.apache.http.client.utils.URIBuilder; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.util.EntityUtils; import java.awt.*; import java.net.URI; @@ -25,23 +23,10 @@ public class MTAUtils { private static String URL = "http://pingtcss.qq.com/pingd"; private static String SID = "500676642"; private static String SI = getI("s"); + private static String version = null; + private static String userAgent = null; private static ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); - private static final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); - - public static CloseableHttpClient getHttpClient() { - - RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(1000) - .setConnectTimeout(1000) - .setSocketTimeout(1000) - .build(); - - return HttpClients.custom() - .setDefaultRequestConfig(requestConfig) - .setConnectionManager(connManager) - .build(); - } public static String getI(String prefix) { int[] b = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; @@ -59,17 +44,15 @@ public static String getI(String prefix) { } public static void click(String actionsId, Config config) { - cachedThreadPool.execute(new ClickTask(getHttpClient(), config, actionsId)); + cachedThreadPool.execute(new ClickTask(config, actionsId)); } private static class ClickTask implements Runnable { - private CloseableHttpClient client; private Config config; private String actionsId; - public ClickTask(CloseableHttpClient client, Config config, String actionsId) { - this.client = client; + public ClickTask(Config config, String actionsId) { this.config = config; this.actionsId = actionsId; } @@ -77,6 +60,19 @@ public ClickTask(CloseableHttpClient client, Config config, String actionsId) { @Override public void run() { try { + if (version == null) { + version = PluginManager.getPlugin(PluginId.getId(Constant.PLUGIN_ID)).getVersion() + .replace("v", "").replaceAll("-|_", "."); + } + if (userAgent == null) { + if (SystemInfo.OS_NAME.toUpperCase().contains("MAC")) { + userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"; + } else if (SystemInfo.OS_NAME.toUpperCase().contains("LINUX")) { + userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"; + } else { + userAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"; + } + } Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); Calendar calendar = Calendar.getInstance(); URI uri = new URIBuilder(URL) @@ -89,7 +85,7 @@ public void run() { .setParameter("rdm", "") .setParameter("rurl", "") .setParameter("rarg", "") - .setParameter("adt", "") + .setParameter("adt", version) .setParameter("r2", SID) .setParameter("scr", (int)screensize.getWidth() + "x" + (int)screensize.getHeight()) .setParameter("scl", Toolkit.getDefaultToolkit().getScreenResolution() + "-bit") @@ -99,10 +95,8 @@ public void run() { .setParameter("random", System.currentTimeMillis() + "") .build(); - HttpGet get = new HttpGet(uri); + HttpRequests.request(uri.toURL().toString()).userAgent(userAgent).tryConnect(); - HttpResponse response = client.execute(get); - EntityUtils.consume(response.getEntity()); } catch (Exception e) { } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java new file mode 100644 index 00000000..9df852a4 --- /dev/null +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java @@ -0,0 +1,695 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.shuzijun.leetcode.plugin.utils.io; + +import com.intellij.Patches; +import com.intellij.ide.IdeBundle; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationInfo; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.SystemProperties; +import com.intellij.util.Url; +import com.intellij.util.io.CountingGZIPInputStream; +import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.net.NetUtils; +import com.intellij.util.net.ssl.CertificateManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.net.ssl.*; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +/** + *

Handy class for reading data from URL connections with built-in support for HTTP redirects and gzipped content and automatic cleanup.

+ * + *

Examples

+ * + *

Reading the whole response into a string:
+ * {@code HttpRequests.request("https://example.com").readString(progressIndicator)}

+ * + *

Downloading a file:
+ * {@code HttpRequests.request("https://example.com/file.zip").saveToFile(new File(downloadDir, "temp.zip"), progressIndicator)}

+ * + *

Tuning a connection:
+ * {@code HttpRequests.request(url).userAgent("IntelliJ").readString()}
+ * {@code HttpRequests.request(url).tuner(connection -> connection.setRequestProperty("X-Custom", value)).readString()}

+ * + *

Using the input stream to implement custom reading logic:
+ * {@code int firstByte = HttpRequests.request("file:///dev/random").connect(request -> request.getInputStream().read())}
+ * {@code String firstLine = HttpRequests.request("https://example.com").connect(request -> new BufferedReader(request.getReader()).readLine())}

+ * + * @see HttpStatusException a sublass of IOException, which includes an actual URL and HTTP response code + * @see + */ +public final class HttpRequests { + private static final Logger LOG = Logger.getInstance(HttpRequests.class); + + public static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8"; + + public static final int CONNECTION_TIMEOUT = SystemProperties.getIntProperty("idea.connection.timeout", 10000); + public static final int READ_TIMEOUT = SystemProperties.getIntProperty("idea.read.timeout", 60000); + public static final int REDIRECT_LIMIT = SystemProperties.getIntProperty("idea.redirect.limit", 10); + + private static final int[] REDIRECTS = { + // temporary redirects + HttpURLConnection.HTTP_MOVED_TEMP, 307 /* temporary redirect */, + // permanent redirects + HttpURLConnection.HTTP_MOVED_PERM, HttpURLConnection.HTTP_SEE_OTHER, 308 /* permanent redirect */ + }; + + private HttpRequests() { } + + public interface Request { + @NotNull + String getURL(); + + @NotNull + URLConnection getConnection() throws IOException; + + @NotNull + InputStream getInputStream() throws IOException; + + @NotNull + BufferedReader getReader() throws IOException; + + @NotNull + BufferedReader getReader(@Nullable ProgressIndicator indicator) throws IOException; + + /** @deprecated Called automatically on open connection. Use {@link RequestBuilder#tryConnect()} to get response code */ + @Deprecated + boolean isSuccessful() throws IOException; + + @NotNull + File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException; + + byte[] readBytes(@Nullable ProgressIndicator indicator) throws IOException; + + @NotNull + String readString(@Nullable ProgressIndicator indicator) throws IOException; + + @NotNull + default String readString() throws IOException { + return readString(null); + } + + @NotNull + CharSequence readChars(@Nullable ProgressIndicator indicator) throws IOException; + + default void write(@NotNull String data) throws IOException { + write(data.getBytes(StandardCharsets.UTF_8)); + } + + default void write(byte[] data) throws IOException { + HttpURLConnection connection = (HttpURLConnection)getConnection(); + connection.setFixedLengthStreamingMode(data.length); + try (OutputStream stream = connection.getOutputStream()) { + stream.write(data); + } + } + } + + public interface RequestProcessor { + T process(@NotNull Request request) throws IOException; + } + + public interface ConnectionTuner { + void tune(@NotNull URLConnection connection) throws IOException; + } + + public static class HttpStatusException extends IOException { + private final int myStatusCode; + private final String myUrl; + + public HttpStatusException(@NotNull String message, int statusCode, @NotNull String url) { + super(message); + myStatusCode = statusCode; + myUrl = url; + } + + public int getStatusCode() { + return myStatusCode; + } + + @NotNull + public String getUrl() { + return myUrl; + } + + @Override + public String toString() { + return super.toString() + ". Status=" + myStatusCode + ", Url=" + myUrl; + } + } + + @NotNull + public static RequestBuilder request(@NotNull Url url) { + return request(url.toExternalForm()); + } + + @NotNull + public static RequestBuilder request(@NotNull String url) { + return new RequestBuilderImpl(url, null); + } + + @NotNull + public static RequestBuilder head(@NotNull String url) { + return new RequestBuilderImpl(url, connection -> ((HttpURLConnection)connection).setRequestMethod("HEAD")); + } + + @NotNull + public static RequestBuilder delete(@NotNull String url) { + return new RequestBuilderImpl(url, connection -> ((HttpURLConnection)connection).setRequestMethod("DELETE")); + } + + @NotNull + public static RequestBuilder delete(@NotNull String url, @Nullable String contentType) { + return requestWithBody(url, "DELETE", contentType, null); + } + + @NotNull + public static RequestBuilder post(@NotNull String url, @Nullable String contentType) { + return requestWithBody(url, "POST", contentType, null); + } + + @NotNull + public static RequestBuilder put(@NotNull String url, @Nullable String contentType) { + return requestWithBody(url, "PUT", contentType, null); + } + + /** + * Java does not support "newer" HTTP methods, so we have to rely on server-side support of `X-HTTP-Method-Override` header to invoke PATCH + * For reasoning see {@link HttpURLConnection#setRequestMethod(String)} + *

+ * TODO: either fiddle with reflection or patch JDK to avoid server reliance + */ + @NotNull + public static RequestBuilder patch(@NotNull String url, @Nullable String contentType) { + return requestWithBody(url, "POST", contentType, + connection -> connection.setRequestProperty("X-HTTP-Method-Override", "PATCH")); + } + + @NotNull + private static RequestBuilder requestWithBody(@NotNull String url, + @NotNull String requestMethod, + @Nullable String contentType, + @Nullable ConnectionTuner tuner) { + return new RequestBuilderImpl(url, rawConnection -> { + HttpURLConnection connection = (HttpURLConnection)rawConnection; + connection.setRequestMethod(requestMethod); + connection.setDoOutput(true); + if (contentType != null) { + connection.setRequestProperty("Content-Type", contentType); + } + if (tuner != null) { + tuner.tune(connection); + } + }); + } + + @NotNull + public static String createErrorMessage(@NotNull IOException e, @NotNull Request request, boolean includeHeaders) { + StringBuilder builder = new StringBuilder(); + + builder.append("Cannot download '").append(request.getURL()).append("': ").append(e.getMessage()); + + try { + URLConnection connection = request.getConnection(); + if (includeHeaders) { + builder.append("\n, headers: ").append(connection.getHeaderFields()); + } + if (connection instanceof HttpURLConnection) { + HttpURLConnection httpConnection = (HttpURLConnection)connection; + builder.append("\n, response: ").append(httpConnection.getResponseCode()).append(' ').append(httpConnection.getResponseMessage()); + } + } + catch (Throwable ignored) { } + + return builder.toString(); + } + + private static class RequestBuilderImpl extends RequestBuilder { + private final String myUrl; + private int myConnectTimeout = CONNECTION_TIMEOUT; + private int myTimeout = READ_TIMEOUT; + private int myRedirectLimit = REDIRECT_LIMIT; + private boolean myGzip = true; + private boolean myForceHttps; + private boolean myUseProxy = true; + private boolean myIsReadResponseOnError; + private HostnameVerifier myHostnameVerifier; + private String myUserAgent; + private String myAccept; + private ConnectionTuner myTuner; + private final ConnectionTuner myInternalTuner; + private boolean myThrowStatusCodeException = true; + + private RequestBuilderImpl(@NotNull String url, @Nullable ConnectionTuner internalTuner) { + myUrl = url; + myInternalTuner = internalTuner; + } + + @Override + public RequestBuilder connectTimeout(int value) { + myConnectTimeout = value; + return this; + } + + @Override + public RequestBuilder readTimeout(int value) { + myTimeout = value; + return this; + } + + @Override + public RequestBuilder redirectLimit(int redirectLimit) { + myRedirectLimit = redirectLimit; + return this; + } + + @Override + public RequestBuilder gzip(boolean value) { + myGzip = value; + return this; + } + + @Override + public RequestBuilder forceHttps(boolean forceHttps) { + myForceHttps = forceHttps; + return this; + } + + @Override + public RequestBuilder useProxy(boolean useProxy) { + myUseProxy = useProxy; + return this; + } + + @Override + public RequestBuilder isReadResponseOnError(boolean isReadResponseOnError) { + myIsReadResponseOnError = isReadResponseOnError; + return this; + } + + @Override + public RequestBuilder hostNameVerifier(@Nullable HostnameVerifier hostnameVerifier) { + myHostnameVerifier = hostnameVerifier; + return this; + } + + @Override + public RequestBuilder userAgent(@Nullable String userAgent) { + myUserAgent = userAgent; + return this; + } + + @Override + public RequestBuilder productNameAsUserAgent() { + Application app = ApplicationManager.getApplication(); + if (app != null && !app.isDisposed()) { + String productName = ApplicationNamesInfo.getInstance().getFullProductName(); + String version = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode(); + return userAgent(productName + '/' + version); + } + else { + return userAgent("IntelliJ"); + } + } + + @Override + public RequestBuilder accept(@Nullable String mimeType) { + myAccept = mimeType; + return this; + } + + @Override + public RequestBuilder tuner(@Nullable ConnectionTuner tuner) { + myTuner = tuner; + return this; + } + + @NotNull + @Override + public RequestBuilder throwStatusCodeException(boolean shouldThrow) { + myThrowStatusCodeException = shouldThrow; + return this; + } + + @Override + public T connect(@NotNull HttpRequests.RequestProcessor processor) throws IOException { + return process(this, processor); + } + } + + private static class RequestImpl implements Request, AutoCloseable { + private final RequestBuilderImpl myBuilder; + private String myUrl; + private URLConnection myConnection; + private InputStream myInputStream; + private BufferedReader myReader; + + private RequestImpl(RequestBuilderImpl builder) { + myBuilder = builder; + myUrl = myBuilder.myUrl; + } + + @NotNull + @Override + public String getURL() { + return myUrl; + } + + @NotNull + @Override + public URLConnection getConnection() throws IOException { + if (myConnection == null) { + myConnection = openConnection(myBuilder, this); + } + return myConnection; + } + + @NotNull + @Override + public InputStream getInputStream() throws IOException { + if (myInputStream == null) { + if(((HttpURLConnection)getConnection()).getResponseCode()>=400){ + myInputStream = ((HttpURLConnection)getConnection()).getErrorStream(); + }else { + myInputStream = getConnection().getInputStream(); + } + if (myBuilder.myGzip && "gzip".equalsIgnoreCase(getConnection().getContentEncoding())) { + myInputStream = CountingGZIPInputStream.create(myInputStream); + } + } + return myInputStream; + } + + @NotNull + @Override + public BufferedReader getReader() throws IOException { + return getReader(null); + } + + @NotNull + @Override + public BufferedReader getReader(@Nullable ProgressIndicator indicator) throws IOException { + if (myReader == null) { + InputStream inputStream = getInputStream(); + if (indicator != null) { + int contentLength = getConnection().getContentLength(); + if (contentLength > 0) { + inputStream = new ProgressMonitorInputStream(indicator, inputStream, contentLength); + } + } + myReader = new BufferedReader(new InputStreamReader(inputStream, getCharset())); + } + return myReader; + } + + @NotNull + private Charset getCharset() throws IOException { + return HttpUrlConnectionUtil.getCharset(getConnection()); + } + + @Override + public boolean isSuccessful() throws IOException { + URLConnection connection = getConnection(); + return !(connection instanceof HttpURLConnection) || ((HttpURLConnection)connection).getResponseCode() == 200; + } + + @Override + public byte[] readBytes(@Nullable ProgressIndicator indicator) throws IOException { + return doReadBytes(indicator).toByteArray(); + } + + @NotNull + private BufferExposingByteArrayOutputStream doReadBytes(@Nullable ProgressIndicator indicator) throws IOException { + return HttpUrlConnectionUtil.readBytes(getInputStream(), getConnection(), indicator); + } + + @NotNull + @Override + public String readString(@Nullable ProgressIndicator indicator) throws IOException { + return HttpUrlConnectionUtil.readString(getInputStream(), getConnection(), indicator); + } + + @NotNull + @Override + public CharSequence readChars(@Nullable ProgressIndicator indicator) throws IOException { + BufferExposingByteArrayOutputStream byteStream = doReadBytes(indicator); + if (byteStream.size() == 0) { + return ArrayUtil.EMPTY_CHAR_SEQUENCE; + } + else { + return getCharset().decode(ByteBuffer.wrap(byteStream.getInternalBuffer(), 0, byteStream.size())); + } + } + + @Override + @NotNull + public File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException { + FileUtilRt.createParentDirs(file); + + boolean deleteFile = true; + try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { + NetUtils.copyStreamContent(indicator, getInputStream(), out, getConnection().getContentLength()); + deleteFile = false; + } + catch (HttpStatusException e) { + throw e; + } + catch (IOException e) { + throw new IOException(createErrorMessage(e, this, false), e); + } + finally { + if (deleteFile) { + FileUtilRt.delete(file); + } + } + + return file; + } + + @Override + public void close() { + StreamUtil.closeStream(myInputStream); + StreamUtil.closeStream(myReader); + if (myConnection instanceof HttpURLConnection) { + ((HttpURLConnection)myConnection).disconnect(); + } + } + } + + private static T process(RequestBuilderImpl builder, RequestProcessor processor) throws IOException { + Application app = ApplicationManager.getApplication(); + LOG.assertTrue(app == null || app.isUnitTestMode() || app.isHeadlessEnvironment() || !app.isReadAccessAllowed(), + "Network shouldn't be accessed in EDT or inside read action"); + + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + if (contextLoader != null && shouldOverrideContextClassLoader()) { + // hack-around for class loader lock in sun.net.www.protocol.http.NegotiateAuthentication (IDEA-131621) + try (URLClassLoader cl = new URLClassLoader(new URL[0], contextLoader)) { + Thread.currentThread().setContextClassLoader(cl); + return doProcess(builder, processor); + } + finally { + Thread.currentThread().setContextClassLoader(contextLoader); + } + } + else { + return doProcess(builder, processor); + } + } + + private static boolean shouldOverrideContextClassLoader() { + return Patches.JDK_BUG_ID_8032832 && + SystemProperties.getBooleanProperty("http.requests.override.context.classloader", true); + } + + private static T doProcess(RequestBuilderImpl builder, RequestProcessor processor) throws IOException { + try (RequestImpl request = new RequestImpl(builder)) { + T result = processor.process(request); + + if (builder.myThrowStatusCodeException) { + URLConnection connection = request.myConnection; + if (connection != null && connection.getDoOutput()) { + // getResponseCode is not checked on connect, because write must be performed before read + HttpURLConnection urlConnection = (HttpURLConnection)connection; + int responseCode = urlConnection.getResponseCode(); + if (responseCode >= 400) { + throwHttpStatusError(urlConnection, request, builder, responseCode); + } + } + } + + return result; + } + } + + private static URLConnection openConnection(RequestBuilderImpl builder, RequestImpl request) throws IOException { + if (builder.myForceHttps && StringUtil.startsWith(request.myUrl, "http:")) { + request.myUrl = "https:" + request.myUrl.substring(5); + } + + for (int i = 0; i < builder.myRedirectLimit; i++) { + String url = request.myUrl; + + final URLConnection connection; + if (!builder.myUseProxy) { + connection = new URL(url).openConnection(Proxy.NO_PROXY); + } + else if (ApplicationManager.getApplication() == null) { + connection = new URL(url).openConnection(); + } + else { + connection = HttpConfigurable.getInstance().openConnection(url); + } + + if (connection instanceof HttpsURLConnection) { + configureSslConnection(url, (HttpsURLConnection)connection); + } + + connection.setConnectTimeout(builder.myConnectTimeout); + connection.setReadTimeout(builder.myTimeout); + + if (builder.myUserAgent != null) { + connection.setRequestProperty("User-Agent", builder.myUserAgent); + } + + if (builder.myHostnameVerifier != null && connection instanceof HttpsURLConnection) { + ((HttpsURLConnection)connection).setHostnameVerifier(builder.myHostnameVerifier); + } + + if (builder.myGzip) { + connection.setRequestProperty("Accept-Encoding", "gzip"); + } + + if (builder.myAccept != null) { + connection.setRequestProperty("Accept", builder.myAccept); + } + + connection.setUseCaches(false); + + if (builder.myInternalTuner != null) { + builder.myInternalTuner.tune(connection); + } + + if (builder.myTuner != null) { + builder.myTuner.tune(connection); + } + + checkRequestHeadersForNulBytes(connection); + + if (!(connection instanceof HttpURLConnection)) { + return connection; + } + + if (connection.getDoOutput()) { + return connection; + } + + HttpURLConnection httpURLConnection = (HttpURLConnection)connection; + String method = httpURLConnection.getRequestMethod(); + + LOG.assertTrue(method.equals("GET") || method.equals("HEAD") || method.equals("DELETE"), + "'" + method + "' not supported; please use GET, HEAD, DELETE, PUT or POST"); + + if (LOG.isDebugEnabled()) { + LOG.debug("connecting to " + url); + } + int responseCode; + try { + responseCode = httpURLConnection.getResponseCode(); + } + catch (SSLHandshakeException e) { + throw !NetUtils.isSniEnabled() ? new SSLException("SSL error probably caused by disabled SNI", e) : e; + } + if (LOG.isDebugEnabled()) { + LOG.debug("response from " + url + ": " + responseCode); + } + + if (responseCode < 200 || responseCode >= 300 && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED) { + if (ArrayUtil.indexOf(REDIRECTS, responseCode) >= 0) { + httpURLConnection.disconnect(); + url = connection.getHeaderField("Location"); + if (LOG.isDebugEnabled()) { + LOG.debug("redirect from " + url + ": " + url); + } + if (url != null) { + request.myUrl = url; + continue; + } + } + + if(builder.myThrowStatusCodeException) { + throwHttpStatusError(httpURLConnection, request, builder, responseCode); + } + } + + return connection; + } + + throw new IOException(IdeBundle.message("error.connection.failed.redirects")); + } + + private static void throwHttpStatusError(HttpURLConnection connection, RequestImpl request, RequestBuilderImpl builder, int responseCode) throws IOException { + String message = null; + if (builder.myIsReadResponseOnError) { + message = HttpUrlConnectionUtil.readString(connection.getErrorStream(), connection, null); + } + if (StringUtil.isEmpty(message)) { + message = "Request failed with status code " + responseCode; + } + connection.disconnect(); + throw new HttpStatusException(message, responseCode, StringUtil.notNullize(request.myUrl, "Empty URL")); + } + + private static void configureSslConnection(@NotNull String url, @NotNull HttpsURLConnection connection) { + if (ApplicationManager.getApplication() == null) { + LOG.info("Application is not initialized yet; Using default SSL configuration to connect to " + url); + return; + } + + try { + SSLSocketFactory factory = CertificateManager.getInstance().getSslContext().getSocketFactory(); + if (factory == null) { + LOG.info("SSLSocketFactory is not defined by the IDE Certificate Manager; Using default SSL configuration to connect to " + url); + } + else { + connection.setSSLSocketFactory(factory); + } + } + catch (Throwable e) { + LOG.info("Problems configuring SSL connection to " + url, e); + } + } + + /* + * Many servers would not process a request and just return 400 (Bad Request) response if any of request headers contains NUL byte. + * This method checks the request and removes invalid headers. + */ + private static void checkRequestHeadersForNulBytes(URLConnection connection) { + for (Map.Entry> header : connection.getRequestProperties().entrySet()) { + for (String headerValue : header.getValue()) { + if (headerValue.indexOf('\0') >= 0) { + connection.setRequestProperty(header.getKey(), null); + LOG.error(String.format("Problem during request to '%s'. Header's '%s' value contains NUL bytes: '%s'. Omitting this header.", + connection.getURL().toString(), header.getKey(), headerValue)); + break; + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java new file mode 100644 index 00000000..3496d0de --- /dev/null +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java @@ -0,0 +1,61 @@ +package com.shuzijun.leetcode.plugin.utils.io; + + +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.net.NetUtils; +import kotlin.text.Charsets; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class HttpUrlConnectionUtil { + + private static final int BLOCK_SIZE = 16 * 1024; + private static final Pattern CHARSET_PATTERN = Pattern.compile("charset=([^;]+)"); + + public static BufferExposingByteArrayOutputStream readBytes( InputStream inputStream, @NotNull URLConnection connection, @Nullable ProgressIndicator progressIndicator) throws IOException, ProcessCanceledException { + int contentLength = connection.getContentLength(); + BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(contentLength > 0 ? contentLength : BLOCK_SIZE); + NetUtils.copyStreamContent(progressIndicator, inputStream, (OutputStream)out, contentLength); + return out; + } + public static String readString(@NotNull InputStream inputStream, @NotNull URLConnection connection, @Nullable ProgressIndicator progressIndicator) throws IOException, ProcessCanceledException { + BufferExposingByteArrayOutputStream byteStream = readBytes(inputStream, connection, progressIndicator); + if(byteStream.size()== 0){ + return ""; + }else { + return new String(byteStream.getInternalBuffer(), 0, byteStream.size(), getCharset(connection)); + } + } + + + public static Charset getCharset(@NotNull URLConnection urlConnection) throws IOException { + String contentType = urlConnection.getContentType(); + if (StringUtils.isBlank(contentType)) { + Matcher m = CHARSET_PATTERN.matcher(contentType); + if (m.find()) { + try { + Charset charset = Charset.forName(StringUtil.unquoteString(m.group(1))); + return charset; + } catch (IllegalArgumentException var5) { + throw new IOException("unknown charset (" + contentType + ')', var5); + } + } + } + return Charsets.UTF_8; + } + + + +} diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java new file mode 100644 index 00000000..f6797a4a --- /dev/null +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java @@ -0,0 +1,70 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.shuzijun.leetcode.plugin.utils.io; + +import com.intellij.openapi.progress.ProgressIndicator; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.InputStream; + +final class ProgressMonitorInputStream extends InputStream { + private final ProgressIndicator indicator; + private final InputStream in; + + private final double available; + private long count; + + ProgressMonitorInputStream(@NotNull ProgressIndicator indicator, @NotNull InputStream in, int length) { + this.indicator = indicator; + this.in = in; + available = length; + } + + @Override + public int read() throws IOException { + int c = in.read(); + updateProgress(c >= 0 ? 1 : 0); + return c; + } + + private void updateProgress(long increment) { + indicator.checkCanceled(); + if (increment > 0) { + count += increment; + if(!indicator.isIndeterminate()) { + indicator.setFraction((double)count / available); + } + } + } + + @Override + public int read(byte[] b) throws IOException { + int r = in.read(b); + updateProgress(r); + return r; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int r = in.read(b, off, len); + updateProgress(r); + return r; + } + + @Override + public long skip(long n) throws IOException { + long r = in.skip(n); + updateProgress(r); + return r; + } + + @Override + public void close() throws IOException { + in.close(); + } + + @Override + public int available() throws IOException { + return in.available(); + } +} \ No newline at end of file diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java new file mode 100644 index 00000000..2a3947fe --- /dev/null +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java @@ -0,0 +1,109 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.shuzijun.leetcode.plugin.utils.io; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.net.ssl.HostnameVerifier; +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URLConnection; + +@SuppressWarnings("BoundedWildcard") +public abstract class RequestBuilder { + public abstract RequestBuilder connectTimeout(int value); + public abstract RequestBuilder readTimeout(int value); + public abstract RequestBuilder redirectLimit(int redirectLimit); + + /** + * Whether gzip encoding supported. Defaults to {@code true}. + */ + public abstract RequestBuilder gzip(boolean value); + + public abstract RequestBuilder forceHttps(boolean forceHttps); + public abstract RequestBuilder useProxy(boolean useProxy); + public abstract RequestBuilder hostNameVerifier(@Nullable HostnameVerifier hostnameVerifier); + public abstract RequestBuilder userAgent(@Nullable String userAgent); + public abstract RequestBuilder productNameAsUserAgent(); + public abstract RequestBuilder accept(@Nullable String mimeType); + public abstract RequestBuilder tuner(@Nullable HttpRequests.ConnectionTuner tuner); + + /** + * Whether to read server response on error. Error message available as {@link HttpRequests.HttpStatusException#getMessage()}. + * Defaults to false. + */ + public abstract RequestBuilder isReadResponseOnError(boolean isReadResponseOnError); + + /** + * Whether to analyze response status code and throw an exception if it's an "error" code. + * Defaults to true. + */ + @NotNull + public abstract RequestBuilder throwStatusCodeException(boolean shouldThrow); + + public abstract T connect(@NotNull HttpRequests.RequestProcessor processor) throws IOException; + + public int tryConnect() throws IOException { + return connect(request -> { + URLConnection connection = request.getConnection(); + return connection instanceof HttpURLConnection ? ((HttpURLConnection)connection).getResponseCode() : -1; + }); + } + + public T connect(@NotNull HttpRequests.RequestProcessor processor, T errorValue, @Nullable Logger logger) { + try { + return connect(processor); + } + catch (Throwable e) { + if (logger != null) { + logger.warn(e); + } + return errorValue; + } + } + + public void saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException { + connect(request -> request.saveToFile(file, indicator)); + } + + public byte[] readBytes(@Nullable ProgressIndicator indicator) throws IOException { + return connect(request -> request.readBytes(indicator)); + } + + @NotNull + public String readString(@Nullable ProgressIndicator indicator) throws IOException { + return connect(request -> request.readString(indicator)); + } + + @NotNull + public String readString() throws IOException { + return readString(null); + } + + @NotNull + public CharSequence readChars(@Nullable ProgressIndicator indicator) throws IOException { + return connect(request -> request.readChars(indicator)); + } + + @NotNull + public CharSequence readChars() throws IOException { + return readChars(null); + } + + public void write(@NotNull String data) throws IOException { + connect(request -> { + request.write(data); + return null; + }); + } + + public void write(byte[] data) throws IOException { + connect(request -> { + request.write(data); + return null; + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/shuzijun/leetcode/plugin/window/SubmissionsPanel.java b/src/main/java/com/shuzijun/leetcode/plugin/window/SubmissionsPanel.java index d249c062..5631df89 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/window/SubmissionsPanel.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/window/SubmissionsPanel.java @@ -11,6 +11,7 @@ import javax.swing.*; import javax.swing.table.AbstractTableModel; +import java.awt.*; import java.text.DateFormat; import java.util.Date; import java.util.List; @@ -27,7 +28,9 @@ public class SubmissionsPanel extends DialogWrapper { public SubmissionsPanel(@Nullable Project project, TableModel tableModel) { super(project, true); - jpanel = new JBPanel(); + jpanel = new JBPanel(new BorderLayout()); + jpanel.setMinimumSize(new Dimension(400, 400)); + jpanel.setPreferredSize(new Dimension(400, 400)); table = new JBTable(tableModel); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getTableHeader().setReorderingAllowed(false); @@ -38,7 +41,7 @@ public SubmissionsPanel(@Nullable Project project, TableModel tableModel) { table.getColumnModel().getColumn(2).setPreferredWidth(100); table.getColumnModel().getColumn(3).setPreferredWidth(200); table.getColumnModel().getColumn(4).setPreferredWidth(100); - jpanel.add(new JBScrollPane(table, JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER)); + jpanel.add(new JBScrollPane(table, JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER); setModal(true); init(); diff --git a/src/main/resources/i18n/info.properties b/src/main/resources/i18n/info.properties index 93d5cd85..02942a5d 100644 --- a/src/main/resources/i18n/info.properties +++ b/src/main/resources/i18n/info.properties @@ -19,6 +19,7 @@ response.timeout=Timed out while waiting for result request.code=Code does not exist request.empty=Empty code(not including comment) request.pending=Code submitted. Please wait... +request.cancel=Request cancelled request.failed=Failed to send request submit.success=Success:\n\tRuntime:{0}, faster than {1}% of {2} online submissions.\n\tMemory Usage:{3}, less than {4}% of {5} online submissions.\n submit.failed=Wrong Answer:\n\tinput:{0}\n\tOutput:{1}\n\tExpected:{2}\n\tstdout:\n\t\t{3}\n diff --git a/src/main/resources/i18n/info_zh.properties b/src/main/resources/i18n/info_zh.properties index 67cbabab..83938de0 100644 --- a/src/main/resources/i18n/info_zh.properties +++ b/src/main/resources/i18n/info_zh.properties @@ -19,6 +19,7 @@ response.timeout=\u7B49\u5F85\u7ED3\u679C\u8D85\u65F6 request.code=\u63D0\u4EA4\u7684\u4EE3\u7801\u4E0D\u5B58\u5728 request.empty=\u63D0\u4EA4\u7684\u4EE3\u7801\u4E3A\u7A7A(\u4E0D\u5305\u542B\u6CE8\u91CA) request.pending=\u5DF2\u63D0\u4EA4,\u8BF7\u7A0D\u7B49 +request.cancel=\u8BF7\u6C42\u5DF2\u53D6\u6D88 request.failed=\u8BF7\u6C42\u5931\u8D25 submit.success=\u89E3\u7B54\u6210\u529F:\n\t\u6267\u884C\u8017\u65F6:{0},\u51FB\u8D25\u4E86{1}% \u7684{2}\u7528\u6237\n\t\u5185\u5B58\u6D88\u8017:{3},\u51FB\u8D25\u4E86{4}% \u7684{5}\u7528\u6237\n submit.failed=\u89E3\u7B54\u5931\u8D25:\n\t\u6D4B\u8BD5\u7528\u4F8B:{0}\n\t\u6D4B\u8BD5\u7ED3\u679C:{1}\n\t\u671F\u671B\u7ED3\u679C:{2}\n\tstdout:\n\t\t{3}\n