Competitive Advent Calendar 8日目

元イベント:http://partake.in/events/ee35b200-e151-44c1-b123-482d0a7447b5

みなさんアルゴリズムとか大会の感想とか競技プログラマっぽいこと書いてるけど万年緑コーダな自分が書くことなんてアレなので8日目はTopcoderのArenaのエディタプラグインについての記事を書きます.
日本語が拙いのでわからなかったりしたらどっか書くか直接聞くとかしてください.


以前,自分はプラグインの簡単な作り方やEclipseCoderの改造の仕方などについて書きました.

Topcoder エディタプラグイン作成 メモ  http://d.hatena.ne.jp/nise_nabe/20101029/1288343389
Topcoder エディタプラグイン作成 メモ2 http://d.hatena.ne.jp/nise_nabe/20110407/1302138385
EclipseCoder改造手順(テキトー)     http://d.hatena.ne.jp/nise_nabe/20110430/1304144638


今回は実際にエディタプラグインを実際に作ってみたものを公開してみます.


プラグインの設定方法などは http://d.hatena.ne.jp/nise_nabe/20101029/1288343389 から抜粋.

使い方

1. アリーナを開く http://www.topcoder.com/contest/arena/ContestAppletProd.jnlp

2. アリーナ上部の「Options」の「Editor」を選択.

3. 「Ediror Preferences」が開かれたら左下の「Add」を選択する.

4. 「Name」はテキトーに,「EntryPoint」は作った実装のクラス名でいい.以上の実装ならばMainとだけ書けば良い.「ClassPath」は実装のクラスファイルのあるディレクトリを選択する.

5. あとは「Practices」でテキトーな部屋に入って問題開いて右上の「Choose your editor」でさっき「Name」のところに入力した名前を選択すれば動く.

とりあえず最初のデフォルトのプラグインを見せておきます.こんな感じです.

作ったものは以下の2つです.

  • エディタの背景に自分のTwitterアイコンを表示するプラグイン「TwiconCoder」
  • Arena内で保存するコードをIdeoneに投稿して保存するプラグイン「IdeoneCoder」

ここから作ったプラグインとコード貼ります.

エディタの背景に自分のTwitterアイコンを表示するプラグイン「TwiconCoder」

みんな多分使ってないデフォルトのエディタの背景に自分のTwitterアイコンを表示するプラグインです.
実際にnise_nabeで動かすと以下のような表示になります.

Swingのリハビリで作ったんで出来がひどゴホンゴホン

仕組みは下記のようになってます.
1. エディタを開いたときにTwitterからアイコンを取得する
2. 取得した画像を背景に表示する
これだけです.

本コードは twitter4j を使用しています.動かす場合は「Editor Preferences」の「Common Classpath」にTwitter4jのライブラリ群があるディレクトリを追加してください.
ライブラリURL:http://twitter4j.org/

あとscreennameの部分を自分のIDにするのを忘れないで下さい.
「Editor Preferences」の「Configure」に設定つくろうと思ったけど時間なくて無理.

必要なもの一覧.

コンパイルするもの

TwiconCoder.java

「Common Classpath」に記載しているディレクトリにいれておくもの

lib/twitter4j-async-2.2.5.jar
lib/twitter4j-core-2.2.5.jar
lib/twitter4j-examples-2.2.5.jar
lib/twitter4j-media-support-2.2.5.jar
lib/twitter4j-stream-2.2.5.jar
コード

gist: https://gist.github.com/1438628

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.border.Border;

import twitter4j.ProfileImage;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;

import com.topcoder.client.contestApplet.editors.Standard.EntryPoint;
import com.topcoder.client.contestApplet.editors.Standard.StandardEditor;
import com.topcoder.client.contestApplet.editors.Standard.StandardEditorPanel;

public class TwiconCoder extends EntryPoint {
    String screenname = "nise_nabe";
    BufferedImage img;

    public TwiconCoder() {
        Twitter t = TwitterFactory.getSingleton();
        try {
            ProfileImage image = t.getProfileImage(screenname, ProfileImage.BIGGER);
            img = ImageIO.read(new URL(image.getURL().toString()));
        } catch (TwitterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public JPanel getEditorPanel() {
        StandardEditorPanel panel = (StandardEditorPanel) super.getEditorPanel();
        JScrollPane scroll = (JScrollPane) panel.getComponent(0);
        scroll.setViewportBorder(new CentredBackgroundBorder(img));
        JViewport viewport = (JViewport) scroll.getComponent(0);
        viewport.setOpaque(false);
        JPanel vpanel = (JPanel) viewport.getComponent(0);
        vpanel.setOpaque(false);
        StandardEditor editor = (StandardEditor) vpanel.getComponent(0);
        editor.setOpaque(false);
        return panel;
    }

    private class CentredBackgroundBorder implements Border {
        private final BufferedImage image;

        public CentredBackgroundBorder(BufferedImage image) {
            this.image = image;
        }

        @Override
        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            x += (width - image.getWidth()) / 2;
            y += (height - image.getHeight()) / 2;
            ((Graphics2D) g).drawRenderedImage(image, AffineTransform.getTranslateInstance(x, y));
        }

        @Override
        public Insets getBorderInsets(Component c) {
            return new Insets(0, 0, 0, 0);
        }

        @Override
        public boolean isBorderOpaque() {
            return true;
        }
    }

}

参考: http://terai.xrea.jp/Swing/CentredBackgroundBorder.html

Arena内で保存するコードをIdeoneに投稿して保存するプラグイン「IdeoneCoder」

見た目ほとんど変わってないですね.でもこれはそこそこ使えるんじゃないかなぁ.
このプラグインは「Save」や「Compile」などの保存ボタンを押すとその度にIdeone.comにコードを送信するプラグインです.
下の細い部分にIdeone.comに送ったコードのリンクを出力します.
ちなみに本番中に使うのは多分まずいと思います.多分いないと思いますけど本番では使わないでください.

仕組みは下記のようになっています.
1. 「save」をクリックする
2. Ideone.comに送信される
3. コードへのリンクが表示される

本コードはApache Axisを使用しています.動かす場合は「Editor Preferences」の「Common Classpath」にApache Axisのライブラリ群があるディレクトリを追加してください.
https://axis.apache.org/axis/

wget http://ftp.kddilabs.jp/infosystems/apache//ws/axis/1_4/axis-bin-1_4.tar.gz
tar zxvf axis-bin-1_4.tar.gz

などとして取得し解凍すると axis-1_4というディレクトリができるので axis-1_4/lib というディレクトリを「Common Classpath」に追加すればおkです.


ここに記載したコードとライブラリ以外に必要なものがあります.それはApache Axisを使ってWSDLJavaに変換したものです.これらは gist に貼ってあるので各自取得してIdeoneCoder.javaと一緒にコンパイルしたものを設定したClasspath直下に置くか,コンパイルして「Common Classpath」に追加してください.
気が向いたらjar化して置いときます.


ideoneのユーザ名とかパスワードも「Editor Preferences」の「Configure」に設定つくろうと思ったけど時間(ry
パスワードは下記URLから下記画像の部分で設定しておくといいよ.
パスワード変更URL: https://ideone.com/account/

必要なもの一覧

コンパイルするもの

IdeoneCoder
com/ideone/Ideone_Service_v1BindingStub.java
com/ideone/Ideone_Service_v1Port_PortType.java
com/ideone/Ideone_Service_v1ServiceLocator.java
com/ideone/Ideone_Service_v1Service.java

「Common Classpath」に記載しているディレクトリにいれておくもの

lib/axis.jar
lib/log4j-1.2.8.jar
lib/commons-logging-1.0.4.jar
lib/commons-discovery-0.2.jar
lib/jaxrpc.jar:wsdl4j-1.5.1.jar
lib/saaj.jar
コード

gist: https://gist.github.com/1438646

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.HashMap;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.xml.rpc.ServiceException;

import com.ideone.Ideone_Service_v1BindingStub;
import com.ideone.Ideone_Service_v1Port_PortType;
import com.ideone.Ideone_Service_v1ServiceLocator;
import com.topcoder.client.contestApplet.editors.Standard.EntryPoint;
import com.topcoder.client.contestApplet.editors.Standard.StandardEditor;
import com.topcoder.client.contestApplet.editors.Standard.StandardEditorPanel;
import com.topcoder.client.contestant.ProblemComponentModel;
import com.topcoder.shared.language.Language;
import com.topcoder.shared.problem.ProblemComponent;
import com.topcoder.shared.problem.Renderer;

public class IdeoneCoder extends EntryPoint{
    private Ideone_Service_v1Port_PortType port;
    private final String user = "nisenabe";
    private final String pass = "ideone_password";
    private int langId;
    private JTextField linkField;

    public IdeoneCoder() {
        try {
            port = new Ideone_Service_v1ServiceLocator().getIdeone_Service_v1Port();
        } catch (ServiceException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public JPanel getEditorPanel() {
        StandardEditorPanel panel = (StandardEditorPanel) super.getEditorPanel();
        JScrollPane scroll = (JScrollPane) panel.getComponent(0);
        JViewport viewport = (JViewport) scroll.getComponent(0);
        JPanel vpanel = (JPanel) viewport.getComponent(0);
        vpanel.setLayout(new BorderLayout());
        for(Component comp : vpanel.getComponents()){
            if (comp instanceof StandardEditor){
                StandardEditor editor = (StandardEditor) comp;
                vpanel.add(editor, BorderLayout.CENTER);
                break;
            }
        }
        linkField = new JTextField();
        linkField.setBackground(Color.black);
        linkField.setForeground(Color.white);
        vpanel.add(linkField, BorderLayout.SOUTH);
        return panel;
    }

    @Override
    public String getSource() {
        String src = super.getSource();
        try {
            Object[] objs = port.createSubmission(user, pass, src, langId, "", false, true);
            HashMap<String, String> map = (HashMap<String, String>) objs[0];
            if(!map.get("error").equals("OK")){
                linkField.setText("error");
                return src;
            }
            String link = map.get("link");
            linkField.setText("http://ideone.com/" + link);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return src;
    }

    public void setProblemComponent(ProblemComponentModel component, Language language, Renderer renderer){
        switch(language.getId()){
        case 1: // java
            langId = 10;
            break;
        case 3: // c++
            langId = 1;
            break;
        case 4: // c#
            langId = 27;
            break;
        case 5: // vb
            langId = 101;
            break;
        }
    }

}

とりあえず今回用に自分が作ったものは以上です.みなさんも自分専用のプラグインを作って Let's enjoy Topcoder!

ここから駄文.

Eclipseで開発してたのでとりあえずCtrl+F11とかでサラっとArena起動できたらいいなぁとか思った
→「Buildpath」に「ContentApplet.jar」を追加
→「RunConfigurations」で「Main Class」に「com.topcoder.client.contestApplet.runner.generic」を指定する

→「Argumetns」の「Program Arguments」で「www.topcoder.com 5001 http://tunnel1.topcoder.com/tunnel?dummy TopCoder」を指定する

→Ctrl+F11でArena起動できるようになって幸せ


プラグイン動かした直後灰色だけどどうにかならんの?
→なんでやろ
→setSource()でエディタに文字突っ込めば回避できるけどなんかなぁ


EntryPointとか継承してるけどこれなに?
→デフォルトのエディタプラグインを流用したかったのでこのまま使うためにいろいろハックした結果こうなりました
→つまりはデフォルトのエディタプラグインを使うのに必要ですはい
→もっとうまい方法知ってたら教えて


IdeoneCoderでIdeone_Service_v1BindingStubとかってどうやって作るの?
→下記のようにコマンドを実行したら作れたよ!

wget http://ftp.kddilabs.jp/infosystems/apache//ws/axis/1_4/axis-bin-1_4.tar.gz
tar zxvf axis-bin-1_4.tar.gz
cd axis-1_4/lib
wget http://ideone.com/api/1/service.wsdl
export CLASSPATH=axis.jar:log4j-1.2.8.jar:commons-logging-1.0.4.jar:commons-discovery-0.2.jar:jaxrpc.jar:wsdl4j-1.5.1.jar:saaj.jar
java org.apache.axis.wsdl.WSDL2Java -p com.ideone service.wsdl
mv com ~/workspace/IdeoneCoder/src/

ほんとうにおわり.