RICOH賞を受賞しました

大学院の有志チームで参加していたRICOH & Java developer challenge2011でRICOH賞をいただきました。
アプリケーション機能は非常にシンプルでしたが、顔認識技術とビジネスモデルとの組み合わせ、アプリケーションとしての完成度が評価されたのではと思います。また、水着の三愛がリコーの傘下といこともあり、画像解析 × ファッションレポート のアプリケーションが受け入れられたのもあるようです。

グランプリの北陸先端技術大学院さんも顔画像技術を使用されており、エンタープライズアプリケーションにおいてもこれらの技術は注目されているのだと感じました。
また、他チームのアイデアや実装は素晴らしく、若い方々の発想力に驚きました。もう少しブラッシュアップすればRICOHからアプリケーションとして出ても良いのではと思われるアプリケーションもあり良い刺激をもらえました。

約8ヶ月ほど、チームで活動をしてきましたが、最後に良い結果が出て良かったです。チームの皆さんありがとうございました。

RICOH & Java Developer Challenge 2011 最終選考 提出しました

大学院の有志メンバと参加しているRICOH & Java Developer Challenge 2011の最終選考に提出できました。
9月末に1次選考(Emulator編)を無事通過し、最終選考へ向けて実機上でMFPアプリケーションを開発してきました。
5月にチームを発足してから半年以上と長期に渡って活動してきましたが、ようやくシンプルながらも面白いモノが完成しました。
このプログラミングコンテストからは技術的な部分のみでなく、チーム活動の進め方という点に関しても多くの得る事あった為、参加して良かったと思います。

最終選考は2012/1/12(木)です。あと、Ustreamでも放送があるそうです。

PSP:Personal Software Process – Lesson7

Lesson3, Lesson5, Lesson6で開発したプログラムをもとに、Lesson7用の仕様を追加していきます。
これにより、自身の計測値さえあれば、開発に対する多少のブレを含めた上での見積時間と見積総行数を見積もることが出来ます。
※ なお、Lesson5, 6に関しては、クラスとして利用出来るよう変更してください。

Lesson7: 「線形回帰パラメータと予測区間を計算する」
● プログラム仕様スクリプト
ASGKIT PROG7を参照のこと

● 実行結果
線形回帰パラメータと予測区間を計算する

rxy: 0.954497
r2: 0.911064
tail area: 0.000018
β0: -22.552533
β1: 1.727932
yk: 644.429384
range: 230.001974
UPI: 874.431357
LPI: 414.42741

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson7: 
 * 線形回帰パラメータと予測区間を計算する
 * 
 */

public class Program7 {

	private int size;
	private double[] data1;
	private double[] data2;
	private double sumData1;
	private double sumData2;
	private double avgData1;
	private double avgData2;
	private double powData1;
	private double powData2;
	private double sumMulti;
	private double beta0;
	private double beta1;
	private double r;
	private double r2;
	private double y;
	private double tailArea;
	private double p;
	private double x;
	private double deviation;
	private double ran;
	private double range;
	private double upi;
	private double lpi;

	public static void main(String[] args) {

		Program7 pg7 = new Program7();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			// 分析対象となるデータ行を指定する
			int data1 = Integer.parseInt(args[1]);
			int data2 = Integer.parseInt(args[2]);

			// 分析対象のLOC
			long num = Long.parseLong(args[3]);

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[data1 - 1]));
				linkData2.add(Double.parseDouble(strrec[data2 - 1]));
			}
			pg7.size = linkData1.size();

			pg7.data1 = new double[pg7.size];
			pg7.data2 = new double[pg7.size];

			// 合計値を計算する
			pg7.sumData1 = pg7.calcSum(linkData1);
			pg7.sumData2 = pg7.calcSum(linkData2);

			// 合計値から平均値を計算する
			pg7.avgData1 = pg7.calcAvg(pg7.sumData1);
			pg7.avgData2 = pg7.calcAvg(pg7.sumData2);

			// X, Yの二乗の合計値、X*Yの合計値を計算する
			pg7.powData1 = pg7.calcSumPow(linkData1);
			pg7.powData2 = pg7.calcSumPow(linkData2);
			pg7.sumMulti = pg7.calcSumMulti(linkData1, linkData2);

			// β1 を計算する
			pg7.beta1 = pg7.calcBeta1();

			// β0 を計算する
			pg7.beta0 = pg7.calcBeta0();

			// R, R2 を計算する
			pg7.r = pg7.calcR();
			pg7.r2 = pg7.calcR2();

			// tail areaを計算する
			pg7.tailArea = pg7.calcTail();

			// yを計算する
			pg7.y = pg7.calcY(num);

			// xを計算する
			pg7.x = pg7.calcX();

			// 標準偏差を計算する
			pg7.deviation = pg7.calcDeviation();

			// Rangeを前計算する
			pg7.ran = pg7.calcRan(num);

			// Rangeを計算する
			pg7.range = pg7.calcRange();

			// UPIを計算する
			pg7.upi = pg7.calcUPI();

			// LPIを計算する
			pg7.lpi = pg7.calcLPI();

			System.out.println("rxy: " + pg7.changeScale(pg7.r, 6));
			System.out.println("r2: " + pg7.changeScale(pg7.r2, 6));
			System.out.println("tail area: "
					+ new DecimalFormat("0.#######").format(pg7.changeScale(
							pg7.tailArea, 8)));
			System.out.println("β0: " + pg7.changeScale(pg7.beta0, 6));
			System.out.println("β1: " + pg7.changeScale(pg7.beta1, 6));
			System.out.println("yk: " + pg7.changeScale(pg7.y, 6));
			System.out.println("range: " + pg7.changeScale(pg7.range, 6));
			System.out.println("UPI: " + pg7.changeScale(pg7.upi, 6));
			System.out.println("LPI: " + pg7.changeScale(pg7.lpi, 6));

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 合計値を計算する
	 * 
	 */
	private Double calcSum(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Double.parseDouble(i.next().toString());
		}

		return sum;

	}

	/**
	 * 平均値を計算する
	 * 
	 */
	private Double calcAvg(Double sum) {

		return sum / size;

	}

	/**
	 * X, Yの2乗合計値を計算する
	 * 
	 */
	private Double calcSumPow(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Math.pow(Double.parseDouble(i.next().toString()), 2);
		}

		return sum;

	}

	/**
	 * X*Yの合計値を計算する
	 * 
	 */
	private Double calcSumMulti(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double sum = 0;
		int i = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			data1[i] = linkData1.pop();
			data2[i] = linkData2.pop();

			sum = sum + (data1[i] * data2[i]);
			i++;
		}

		return sum;

	}

	/**
	 * β1 を計算する
	 * 
	 */
	private Double calcBeta1() {

		double num1 = (sumMulti - (size * avgData1 * avgData2));
		double num2 = (powData1 - (size * avgData1 * avgData1));

		return num1 / num2;

	}

	/**
	 * β0 を計算する
	 * 
	 */
	private Double calcBeta0() {

		return avgData2 - (beta1 * avgData1);

	}

	/**
	 * R を計算する
	 * 
	 */
	private Double calcR() {

		double num1 = (size * sumMulti) - (sumData1 * sumData2);

		double num21 = (size * powData1) - (sumData1 * sumData1);
		double num22 = (size * powData2) - (sumData2 * sumData2);

		return num1 / (Math.sqrt(num21 * num22));

	}

	/**
	 * R2 を計算する
	 * 
	 */
	private Double calcR2() {

		return r * r;

	}

	/**
	 * Tail Area を計算する
	 * 
	 */
	private Double calcTail() {

		double num1 = r * Math.sqrt(size - 2);
		double num2 = Math.sqrt(1 - r * r);

		Tdist tDist = new Tdist();
		tDist.start(num1 / num2, size - 2);
		p = tDist.getP();

		return (double) (1 - 2 * p);

	}

	/**
	 * Y を計算する
	 * 
	 */
	private Double calcY(long num) {

		return beta0 + beta1 * num;

	}

	/**
	 * PからXを計算する
	 * 
	 */
	private Double calcX() {

		RevTdist RevTdist = new RevTdist();
		RevTdist.start(0.0, size - 2, 0.35);
		double x = RevTdist.getX();

		return x;

	}

	/**
	 * 標準偏差を計算する
	 * 
	 */
	private Double calcDeviation() {

		double sum = 0;
		for (int i = 0; i < data1.length; i++) {
			sum += Math.pow((data2[i] - beta0 - beta1 * data1[i]), 2);
		}

		double dist = sum / (size - 2);
		double dev = Math.sqrt(dist);

		return dev;

	}

	/**
	 * Rangeを前計算する
	 * 
	 */
	private Double calcRan(long num) {

		double num1 = Math.pow(num - avgData1, 2);

		double num2 = 0;
		for (int i = 0; i < data1.length; i++) {
			num2 += Math.pow(data1[i] - avgData1, 2);
		}

		double sq = 1 + (double) 1 / size + (double) (num1 / num2);

		return Math.sqrt(sq);

	}

	/**
	 * Rangeを計算する
	 * 
	 */
	private Double calcRange() {

		return x * deviation * ran;

	}

	/**
	 * UPIを計算する
	 * 
	 */
	private Double calcUPI() {

		return y + range;

	}

	/**
	 * LPIを計算する
	 * 
	 */
	private Double calcLPI() {

		return y - range;

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson6

Lesson5で開発したプログラムをもとに、Lesson6用の要件を追加します。

Lesson6: 「t分布関数を0.0からxの範囲で積分した結果がpとなるようなxを求める」
● プログラム仕様スクリプト
ASGKIT PROG6を参照のこと

● 実行結果
正しい積分結果値よりxをを絞り込みながら調整しつつ、シンプソン法の最適幅、積分結果、t分布関数値を得る

OK! [W=2]
Result P: 0.0
NG! [W=2] 0.062143
NG! [W=4] 0.000017
OK! [W=8]
Result P: 0.178335
NG! [W=2] 0.116564
NG! [W=4] 0.000061
OK! [W=8]
Result P: 0.31305
NG! [W=2] 0.145412
NG! [W=4] 0.00164
OK! [W=8]
Result P: 0.396
NG! [W=2] 0.139119
NG! [W=4] 0.008551
NG! [W=8] 0.000075
OK! [W=16]
Result P: 0.441942
NG! [W=2] 0.10295
NG! [W=4] 0.021035
NG! [W=8] 0.000398
OK! [W=16]
Result P: 0.466616
NG! [W=2] 0.048413
NG! [W=4] 0.035425
NG! [W=8] 0.001493
OK! [W=16]
Result P: 0.480029
NG! [W=2] 0.014094
NG! [W=4] 0.047057
NG! [W=8] 0.003926
NG! [W=16] 0.00001
OK! [W=32]
Result P: 0.487552
NG! [W=2] 0.077696
NG! [W=4] 0.052655
NG! [W=8] 0.007991
NG! [W=16] 0.00004
OK! [W=32]
Result P: 0.491935
NG! [W=2] 0.13875
NG! [W=4] 0.050897
NG! [W=8] 0.013594
NG! [W=16] 0.000132
OK! [W=32]
Result P: 0.494589
NG! [W=2] 0.195789
NG! [W=4] 0.041945
NG! [W=8] 0.020339
NG! [W=16] 0.000344
OK! [W=32]
Result P: 0.496255
NG! [W=2] 0.13875
NG! [W=4] 0.050897
NG! [W=8] 0.013594
NG! [W=16] 0.000132
OK! [W=32]
Result P: 0.494589
NG! [W=2] 0.167808
NG! [W=4] 0.04727
NG! [W=8] 0.016859
NG! [W=16] 0.000218
OK! [W=32]
Result P: 0.495514
NG! [W=2] 0.153413
NG! [W=4] 0.049303
NG! [W=8] 0.015197
NG! [W=16] 0.000171
OK! [W=32]
Result P: 0.495078
NG! [W=2] 0.146114
NG! [W=4] 0.050156
NG! [W=8] 0.014388
NG! [W=16] 0.000151
OK! [W=32]
Result P: 0.49484
NG! [W=2] 0.149772
NG! [W=4] 0.049743
NG! [W=8] 0.01479
NG! [W=16] 0.000161
OK! [W=32]
Result P: 0.494961
NG! [W=2] 0.151592
NG! [W=4] 0.049525
NG! [W=8] 0.014993
NG! [W=16] 0.000165
OK! [W=32]
Result P: 0.49502
NG! [W=2] 0.150681
NG! [W=4] 0.049634
NG! [W=8] 0.014892
NG! [W=16] 0.000162
OK! [W=32]
Result P: 0.49499
NG! [W=2] 0.151136
NG! [W=4] 0.049578
NG! [W=8] 0.014943
NG! [W=16] 0.000164
OK! [W=32]
Result P: 0.495005
NG! [W=2] 0.150912
NG! [W=4] 0.049609
NG! [W=8] 0.014916
NG! [W=16] 0.000164
OK! [W=32]
Result P: 0.494998
NG! [W=2] 0.151025
NG! [W=4] 0.049595
NG! [W=8] 0.014929
NG! [W=16] 0.000163
OK! [W=32]
Result P: 0.495001
NG! [W=2] 0.150967
NG! [W=4] 0.049602
NG! [W=8] 0.014923
NG! [W=16] 0.000163
OK! [W=32]
Result P: 0.495
Result X: 4.60400390625

● ソースコード

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * PSP Lesson6:
 * t分布関数を0.0からxの範囲で積分した結果がpとなるようなxを求める
 */

public class Program6 {

    private double x;
    private int dof;
    private double p;
    private double d;
    private int W;
    private int initW;
    private double E;
    private int checkF1;
    private int checkF2;
    private long adjustCount;

    public Program6() {
        initW = 1;
        W = initW;
        E = 0.00001;
        d = 0.5;
        checkF1 = 0;
        checkF2 = 0;
        adjustCount = 0;
    }

    public static void main(String[] args) {

        Program6 pg6 = new Program6();

        Double doubleArray[] = new Double[2];

        // 実行時引数からx(trial value), dof, pを取得する
        pg6.x = Double.parseDouble(args[0]);
        pg6.dof = Integer.parseInt(args[1]);
        pg6.p = Double.parseDouble(args[2]);
        boolean loopF = true;

        while (loopF) {

            for (int i = 0; i < 2; i++) {

                doubleArray[i] = (double) 0;

                // wを計算する
                double w = pg6.x / pg6.W;

                for (int j = 0; j <= pg6.W; j++) {

                    // p2を計算する
                    double p2 = 1 + (Math.pow((double) w * j, 2) / pg6.dof);
                    p2 = pg6.changeScale(p2, 6);

                    // p3を計算する
                    double p3 = Math.pow(p2, -(double) (pg6.dof + 1) / 2);
                    p3 = pg6.changeScale(p3, 6);

                    // p4を計算する
                    double p4 = pg6.calcP4(p3, pg6.dof);
                    p4 = pg6.changeScale(p4, 6);

                    // p5を計算する
                    double p5 = p3 * p4;
                    p5 = pg6.changeScale(p5, 6);

                    // p6を計算する
                    double p6 = pg6.calcP6(w, p5, j);

                    doubleArray[i] += p6;

                }

                doubleArray[i] = pg6.changeScale(doubleArray[i], 6);

                if (i == 0) {
                    pg6.W = pg6.W * 2;
                } else {
                    if ((doubleArray[i - 1] - doubleArray[i]) < pg6.E
                            && (doubleArray[i] - doubleArray[i - 1]) < pg6.E) {
                        System.out.println("OK! [W=" + pg6.W + "]");
                        System.out.println("Result P: " + doubleArray[i]);

                        if (doubleArray[i] != pg6.p) {
                            pg6.adjustD(doubleArray[i]);
                            pg6.x += pg6.d;
                            pg6.W = pg6.initW;
                            i = -1;
                        } else {
                            System.out.println("Result X: " + pg6.x);
                            loopF = false;
                        }
                    } else {
                        if (doubleArray[i - 1] > doubleArray[i]) {
                            System.out.println("NG! [W="
                                    + pg6.W
                                    + "] "
                                    + new DecimalFormat("0.######")
                                            .format(doubleArray[i - 1]
                                                    - doubleArray[i]));
                        } else {
                            System.out.println("NG! [W="
                                    + pg6.W
                                    + "] "
                                    + new DecimalFormat("0.######")
                                            .format(doubleArray[i]
                                                    - doubleArray[i - 1]));
                        }
                        // 結果保持
                        doubleArray[i - 1] = doubleArray[i];

                        // シンプソン法適用時の幅拡張
                        pg6.W = pg6.W * 2;
                        i = 0;
                    }
                }

            }
        }
    }

    /**
     * dを調整する
     * 
     */
    private void adjustD(Double result) {

        adjustCount += 1;
        double w = (double) (result - p);

        if (d < 0) {
            d = -d;
        }
        if (adjustCount == 1) {
            if (w > 0) {
                checkF1 = -1;
                checkF2 = -1;
            } else {
                checkF1 = 1;
                checkF2 = 1;
            }
        }

        // 正しい積分結果値(P)を通り越えるまでは、0.5ポイント単位で増減させる
        // 通過後は、より詳細に増減させて積分結果値(P)に近づくように調整する
        if (checkF1 == checkF2) {
            if (w > 0) {
                d = -0.5;
                checkF2 = -1;
            } else {
                d = 0.5;
                checkF2 = 1;
            }
        } else {
            if (w > 0) {
                d /= 2;
                d = -d;
            } else {
                d /= 2;
            }
        }

    }

    /**
     * p4を計算する
     * 
     */
    private Double calcP4(Double p3, int dof) {

        double base = fact(((double) (this.dof + 1) / 2) - 1);
        double frac = fact((double) this.dof / 2 - 1.0);
        return base / (Math.pow(this.dof * Math.PI, 0.5) * frac);

    }

    /**
     * p6を計算する
     * 
     */
    private Double calcP6(Double w, Double p5, int i) {

        int multi = 0;

        if (i == 0 || i == this.W) {
            multi = 1;
        // 偶数の場合はmulti = 2
        } else if (i % 2 == 0) {
            multi = 2;
        // 奇数の場合はmulti = 4
        } else {
            multi = 4;
        }

        return (double) (w / 3) * multi * p5;

    }

    /**
     * 階乗計算
     * 
     */
    private double fact(double i) {

        if (i == 1) {
            return 1;
        } else {
            if (i == 0) {
                return i;
            } else if (i < 1) {
                return i * Math.sqrt(Math.PI);
            } else {
                // 再帰呼び出しする
                return i * fact(i - 1);
            }
        }

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

PSP:Personal Software Process – Lesson5

Lesson5: 「シンプソン法を使用して、t分布関数を数値的に分析する」
● プログラム仕様スクリプト
ASGKIT PROG5を参照のこと

● 実行結果
シンプソン法の幅をより詳細に設定していき、差分が0.00001以下であれば適切な幅とみなし終了する。

NG! [W=2] 0.094385
NG! [W=4] 0.02666
NG! [W=8] 0.000162
OK! [W=16]
Result: 0.494999

● ソースコード

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * PSP Lesson5:
 * シンプソン法を使用して、t分布関数を数値的に分析する
 */

public class Program5 {

    private double x;
    private int dof;
    private int W;
    private double E;

    public static void main(String[] args) {

        Program5 pg5 = new Program5();

        Double doubleArray[] = new Double[2];

        // 実行時引数からx, dof, Wを取得する
        pg5.x = Double.parseDouble(args[0]);
        pg5.dof = Integer.parseInt(args[1]);
        pg5.W = Integer.parseInt(args[2]);
        pg5.E = 0.00001;

        for (int i = 0; i < 2; i++) {

            doubleArray[i] = (double) 0;

            // wを計算する
            double w = pg5.x / pg5.W;

            for (int j = 0; j <= pg5.W; j++) {

                // p2を計算する
                double p2 = 1 + (Math.pow((double) w * j, 2) / pg5.dof);
                p2 = pg5.changeScale(p2, 6);

                // p3を計算する
                double p3 = Math.pow(p2, -(double) (pg5.dof + 1) / 2);
                p3 = pg5.changeScale(p3, 6);

                // p4を計算する
                double p4 = pg5.calcP4(p3, pg5.dof);
                p4 = pg5.changeScale(p4, 6);

                // p5を計算する
                double p5 = p3 * p4;
                p5 = pg5.changeScale(p5, 6);

                // p6を計算する
                double p6 = pg5.calcP6(w, p5, j);

                doubleArray[i] += p6;

            }

            doubleArray[i] = pg5.changeScale(doubleArray[i], 6);

            if (i == 0) {
                pg5.W = pg5.W * 2;
            } else {
                if ((doubleArray[i-1] - doubleArray[i]) < pg5.E
                        && (doubleArray[i] - doubleArray[i-1]) < pg5.E) {
                    System.out.println("OK! [W=" + pg5.W + "]");
                    System.out.println("Result: " + doubleArray[i]);
                } else {
                    if (doubleArray[i-1] > doubleArray[i]) {
                        System.out
                                .println("NG! [W="
                                        + pg5.W
                                        + "] "
                                        + new DecimalFormat("0.######")
                                                .format(doubleArray[i-1]
                                                        - doubleArray[i]));
                    } else {
                        System.out
                                .println("NG! [W="
                                        + pg5.W
                                        + "] "
                                        + new DecimalFormat("0.######")
                                                .format(doubleArray[i]
                                                        - doubleArray[i-1]));
                    }
                    // 結果保持
                    doubleArray[i - 1] = doubleArray[i];

                    // シンプソン法適用時の幅拡張
                    pg5.W = pg5.W * 2;
                    i = 0;
                }
            }
        }
    }

    /**
     * p4を計算する
     * 
     */
    private Double calcP4(Double p3, int dof) {

        double base = fact(((double) (this.dof + 1) / 2) - 1);
        double frac = fact((double) this.dof / 2 - 1.0);
        return base / (Math.pow(this.dof * Math.PI, 0.5) * frac);

    }

    /**
     * p6を計算する
     * 
     */
    private Double calcP6(Double w, Double p5, int i) {

        int multi = 0;

        if (i == 0 || i == this.W) {
            multi = 1;
            // 偶数の場合はmulti = 2
        } else if (i % 2 == 0) {
            multi = 2;
            // 奇数の場合はmulti = 4
        } else {
            multi = 4;
        }

        return (double) (w / 3) * multi * p5;

    }

    /**
     * 階乗計算
     * 
     */
    private double fact(double i) {

        if (i == 1) {
            return 1;
        } else {
            if (i == 0) {
                return i;
            } else if (i < 1) {
                return i * Math.sqrt(Math.PI);
            } else {
                // 再帰呼び出しする
                return i * fact(i - 1);
            }
        }

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

PSP:Personal Software Process – Lesson4

Lesson4: 「ファイルから数値データのリストを読み込み、相対規模表を作成する」
● プログラム仕様スクリプト
ASGKIT PROG4を参照のこと

● 実行結果
シンプソン法の幅をより詳細に設定していき、差分が0.00001以下であれば適切な幅とみなし終了する。

Very Small: 6.3375
Small: 8.4393
Medium: 11.2381
Large: 14.965
Very Large 19.928

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson4: 
 * ファイルから数値データのリストを読み込み、相対規模表を作成する
 */

public class Program4 {

	private int size;
	private LinkedList<Double> linkArray;
	private LinkedList<Double> logArray;

	private double vs;
	private double s;
	private double m;
	private double l;
	private double vl;

	public static void main(String[] args) {

		Program4 pg4 = new Program4();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[1]));
				linkData2.add(Double.parseDouble(strrec[2]));
			}
			pg4.size = linkData1.size();

			// アイテム当たりの規模を計算する
			pg4.linkArray = new LinkedList<Double>();
			pg4.calcVol(linkData1, linkData2);

			// アイテム毎のln(x)を計算する
			pg4.logArray = new LinkedList<Double>();
			Double lnSum = pg4.calcLn();

			// ln(x)の平均値を計算する
			Double lnAvg = pg4.calcLnAvg(lnSum);

			// 平均からの対数値の分散を計算する
			double lnPowSum = pg4.calcSumPow(lnAvg);

			// 標準偏差を計算する
			double lnDeviation = pg4.calcDeviation(lnPowSum);

			// 規模範囲を計算する
			pg4.calcScale(lnAvg, lnDeviation);

			System.out.println("Very Small: " + pg4.vs);
			System.out.println("Small: " + pg4.s);
			System.out.println("Medium: " + pg4.m);
			System.out.println("Large: " + pg4.l);
			System.out.println("Very Large " + pg4.vl);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * アイテム当たりの規模を計算する
	 * 
	 */
	private void calcVol(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double i = 0;
		double j = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			i = linkData1.pop();
			j = linkData2.pop();

			this.linkArray.add(this.changeScale(i / j, 4));

		}

	}

	/**
	 * アイテム毎のln(x)を計算する
	 * 
	 */
	private Double calcLn() {

		double j = 0;
		double logSum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {

			j = this.changeScale(
					Math.log(Double.parseDouble(i.next().toString())), 6);
			this.logArray.add(j);

			// 合計値も計算する
			logSum += j;
		}

		return this.changeScale(logSum, 5);

	}

	/**
	 * ln(x)の平均値を計算する
	 * 
	 */
	private Double calcLnAvg(Double sum) {

		return this.changeScale(sum / size, 6);

	}

	/**
	 * 平均からの対数値の分散を計算する
	 * 
	 */
	private Double calcSumPow(double calclnAvg) {

		double sum = 0;
		for (Iterator i = logArray.iterator(); i.hasNext();) {
			sum = sum
					+ Math.pow(Double.parseDouble(i.next().toString())
							- calclnAvg, 2);
		}

		return this.changeScale(sum, 6);

	}

	/**
	 * lnの標準偏差を計算する
	 * 
	 */
	private Double calcDeviation(Double sum) {

		return this.changeScale(Math.sqrt(sum / (size - 1)), 6);

	}

	/**
	 * 規模範囲を計算する
	 * 
	 */
	private void calcScale(double lnAvg, double deviation) {

		vs = this.changeScale(Math.exp(lnAvg - (2 * deviation)), 4);
		s = this.changeScale(Math.exp(lnAvg - deviation), 4);
		m = this.changeScale(Math.exp(lnAvg), 4);
		l = this.changeScale(Math.exp(lnAvg + deviation), 4);
		vl = this.changeScale(Math.exp(lnAvg + (2 * deviation)), 4);

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson3

Lesson1, Lesson3に続いて、進めていきます。
各回のスクリプトはSoftware Engineering Instituteよりダウンロード可能です。

Lesson3: 「ファイルから数値データのリストを読み込み、相関係数と線形回帰パラメータを計算し、時間等の見積を行う」
● プログラム仕様スクリプト
ASGKIT PROG3を参照のこと

● 実行結果

β0: -22.54
β1: 1.7279
r: 0.9545
r2: 0.9111
y: 644.4294

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson3:
 * ファイルから数値データのリストを読み込み、相関係数と線形回帰パラメータを計算し、時間等の見積を行う
 */

public class Program3 {

	private int size;
	private double sumData1;
	private double sumData2;
	private double avgData1;
	private double avgData2;
	private double powData1;
	private double powData2;
	private double sumMulti;
	private double beta0;
	private double beta1;
	private double r;
	private double r2;
	private double y;

	public static void main(String[] args) {

		Program3 pg3 = new Program3();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			// 分析対象となるデータ行を指定する
			int data1 = Integer.parseInt(args[1]);
			int data2 = Integer.parseInt(args[2]);

			// 分析対象のLOC
			long num = Long.parseLong(args[3]);

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[data1 - 1]));
				linkData2.add(Double.parseDouble(strrec[data2 - 1]));
			}
			pg3.size = linkData1.size();

			// 合計値を計算する
			pg3.sumData1 = pg3.calcSum(linkData1);
			pg3.sumData2 = pg3.calcSum(linkData2);

			// 合計値から平均値を計算する
			pg3.avgData1 = pg3.calcAvg(pg3.sumData1);
			pg3.avgData2 = pg3.calcAvg(pg3.sumData2);

			// X, Yの二乗の合計値、X*Yの合計値を計算する
			pg3.powData1 = pg3.calcSumPow(linkData1);
			pg3.powData2 = pg3.calcSumPow(linkData2);
			pg3.sumMulti = pg3.calcSumMulti(linkData1, linkData2);

			// β1 を計算する
			pg3.beta1 = pg3.calcBeta1();

			// β0 を計算する
			pg3.beta0 = pg3.calcBeta0();

			// R, R2 を計算する
			pg3.r = pg3.calcR();
			pg3.r2 = pg3.calcR2();

			// 最終的な結果を得る
			pg3.y = pg3.calcY(num);

			System.out.println("β0: " + pg3.beta0);
			System.out.println("β1: " + pg3.beta1);
			System.out.println("r: " + pg3.r);
			System.out.println("r2: " + pg3.r2);
			System.out.println("y: " + pg3.y);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 合計値を計算する
	 * 
	 */
	private Double calcSum(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Double.parseDouble(i.next().toString());
		}

		return sum;

	}

	/**
	 * 平均値を計算する
	 * 
	 */
	private Double calcAvg(Double sum) {

		return this.changeScale(sum / size, 2);

	}

	/**
	 * X, Yの2乗合計値を計算する
	 * 
	 */
	private Double calcSumPow(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Math.pow(Double.parseDouble(i.next().toString()), 2);
		}

		return sum;

	}

	/**
	 * X*Yの合計値を計算する
	 * 
	 */
	private Double calcSumMulti(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double sum = 0;
		double i = 0;
		double j = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			i = linkData1.pop();
			j = linkData2.pop();

			sum = sum + (i * j);
		}

		return sum;

	}

	/**
	 * β1 を計算する
	 * 
	 */
	private Double calcBeta1() {

		double num1 = (sumMulti - (size * avgData1 * avgData2));
		double num2 = (powData1 - (size * avgData1 * avgData1));

		return this.changeScale(num1 / num2, 4);

	}

	/**
	 * β0 を計算する
	 * 
	 */
	private Double calcBeta0() {

		return this.changeScale(avgData2 - (beta1 * avgData1), 2);

	}

	/**
	 * R を計算する
	 * 
	 */
	private Double calcR() {

		double num1 = (size * sumMulti) - (sumData1 * sumData2);

		double num21 = (size * powData1) - (sumData1 * sumData1);
		double num22 = (size * powData2) - (sumData2 * sumData2);

		return this.changeScale(num1 / (Math.sqrt(num21 * num22)), 4);

	}

	/**
	 * R2 を計算する
	 * 
	 */
	private Double calcR2() {

		return this.changeScale(r * r, 4);

	}

	/**
	 * Y を計算する
	 * 
	 */
	private Double calcY(long num) {

		return this.changeScale(beta0 + beta1 * num, 4);

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson2

前回に続いて、Lesson2を実施しました。
分析結果は特に載せていませんが、簡単なプログラムでもバグがあったり、結構時間を食ったりしました。
なお、PSPではコーディングに凝り過ぎる必要は無いと明記していますので、大まかな感じでOKです。
但し、コーディングフェーズを終了する際は要件が確実に含まれているか、コードエラーがないかなどのチェックを設けてる必要があります。

Lesson2: 「ソースファイルの行数、クラ数数、メソッド数をカウントする」
● プログラム仕様スクリプト
ASGKIT PROG2を参照のこと

● 実行結果

Part Name: Program3
Number of Items: 11
Part Size: 112
Total Size: 122

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;

/**
 * PSP Lesson2: 
 * ソフトウェア開発プロセス特論 演習1 Date 2011/10/13 ソースファイルの行数、クラス数、メソッド数をカウントする
 */
public class Program2 extends StreamTokenizer {

	private Boolean wordBool = false;
	private String beforeWord = null;
	private String className = null;
	private int lineSize = 0;
	private int totalSize = 0;
	private int methodCnt = 0;
	ArrayList<String> classArray = new ArrayList<String>();
	ArrayList<String> methodArray = new ArrayList<String>();

	public Program2(Reader r) {
		super(r);

		this.eolIsSignificant(true); // 改行コードを検出する
		this.slashStarComments(true); // /* */ 型のコメント処理を有効にする
		this.slashSlashComments(true); // //型コメントの処理を有効にする
		this.wordChars('_', '_'); // 英数字の他に識別子に使える文字の定義
		this.parseNumbers(); // 数値リテラルを解析する

		classArray.add("class");
		classArray.add("interface");
		methodArray.add("void");
	}

	public static void main(String[] args) {

		Reader reader = null;

		try {
			reader = new BufferedReader(new FileReader(new File(args[0])));
			Program2 pg2 = new Program2(reader);
			pg2.checkCnt();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	void checkCnt() throws IOException {
		while (this.nextToken() != TT_EOF) {
			switch (this.ttype) {
			case TT_EOL: // 改行コードの検出
				if (this.wordBool == true) {
					this.lineSize++;
					this.wordBool = false;
				}
				this.beforeWord = "r";
				break;
			case TT_WORD: // 語の検出(キーワードを含む)
				this.wordBool = true;

				if (this.beforeWord != null) {
					// method件数をカウントする (コンストラクタは対応出来ていない)
					if (this.methodArray.contains(this.sval)
							|| this.beforeWord.equals("return")) {
						this.methodCnt++;
					}

					// class名を取得する, 複数class対応
					if (this.classArray.contains(this.beforeWord)) {
						this.className = this.sval;
						if (this.totalSize > 0) {
							this.print();
						}
						this.initialize();
					}
				}
				this.beforeWord = this.sval;

				break;
			default: // その他のトークン {}()[]+-=*/<>,;:
				this.wordBool = true;
				this.beforeWord = "symbol";
				break;
			}
		}

		// 最終行に改行が無いケースの対応
		if (this.beforeWord.equals("symbol")) {
			this.lineSize++;
		}

		this.totalSize += this.lineSize;

		this.print();
		System.out.println("Total Size: " + this.totalSize);

	}

	private void print() {

		System.out.println("Part Name: " + this.className);
		System.out.println("Number of Items: " + this.methodCnt);
		System.out.println("Part Size: " + this.lineSize);

	}

	private void initialize() {

		this.totalSize += this.lineSize;
		this.lineSize = 0;
		this.methodCnt = 0;

	}
}

PSP:Personal Software Process – Lesson1

PSP: Personal Software Process とは、ソフトウェア開発効率の向上の為に、個人毎のソフトウェア開発プロセスを見直しする枠組みです。
PSPはグローバルに多くの大学やソフトウェア開発会社で適用しており、マイクロソフトやボーイングでも使用しているそうです。
個々がPSPによって、自分の開発時の癖やパフォーマンスを把握・改善を行い、開発効率向上を行います。
最終的には全チームメンバがPSPを実施し、TSP:Team Software Process へと発展させます。

今回はPSPガイドブック ソフトウェアエンジニア自己改善 (IT Architects’ Archive)に沿いながら、8本ほどのプログラムを開発し、開発プロセスの見直しをしました。
各プログラムは小さいものばかりで、コーディング自体は2時間程あればコーディング出来ると思います。
今回使用する言語はJAVAを選択しましたが、PHPやPascal、Cでも何でも大丈夫です。
但し、狙いはコーディングではなく、実装前の全体計画や設計、テスト、バグの傾向等を含めた事後分析と多岐に俯瞰する必要があります。
なお、各回のスクリプトはSoftware Engineering Instituteよりダウンロード可能です。

Lesson1: 「ファイルから数値データのリストを読み込み、データの平均値と標準偏差を計算する」
● プログラム仕様スクリプト
ASGKIT PROG1を参照のこと

● 実行結果

Average: 60.32
Standard Deviation: 62.26

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson1:
 * ファイルから数値データのリストを読み込み、データの平均値と標準偏差を計算する
 */

public class Program1 {

    private LinkedList<Double> linkArray;
    private double resultAvg;
    private double resultStgDeviation;

    public static void main(String[] args) {

        Program1 pg1 = new Program1();

        try {
            File f = new File(args[0]);
            FileInputStream fi = new FileInputStream(f);
            BufferedReader br = new BufferedReader(new InputStreamReader(fi,
                    "UTF-8"));
            pg1.linkArray = new LinkedList<Double>();
            String line;
            while ((line = br.readLine()) != null) {
                pg1.linkArray.add(Double.parseDouble(line));
            }

            pg1.calcAvg();
            pg1.calcStgDeviation();

            System.out.println("Average: " + pg1.resultAvg);
            System.out.println("Standard Deviation: " + pg1.resultStgDeviation);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 平均値を計算する
     * 
     */
    private void calcAvg() {

        double avg = 0;
        for (Iterator i = linkArray.iterator(); i.hasNext();) {
            avg = avg + Double.parseDouble(i.next().toString());
        }

        resultAvg = this.changeScale(avg / linkArray.size(), 2);

    }

    /**
     * 標準偏差を計算する
     * 
     */
    private void calcStgDeviation() {

        // 1. データ毎に平均値との差分を2乗し合計値を取る
        // 2. 合計値をデータ個数-1で除算し、平方根を計算する
        double wkStg = 0;
        for (Iterator i = linkArray.iterator(); i.hasNext();) {
            wkStg = wkStg
                    + Math.pow(Double.parseDouble(i.next().toString())
                            - resultAvg, 2);
        }

        resultStgDeviation = this.changeScale(
                Math.sqrt(wkStg / (linkArray.size() - 1)), 2);

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

RICOH & Java Developer Challenge 2011 提出しました

大学院の有志メンバと参加しているRICOH & Java Developer Challenge 2011でアプリケーション、ドキュメントを無事提出出来ました。
企画段階から入れると約4ヶ月掛けて、進めてきました。大学院や仕事をしながらの作業であった為、
非常に疲れましたが、チームとしてはシンプルながらも当初の案はアプリケーションに落とし込めたと思います。
また、個人としては、Javaに慣れ親しむという目標が達成出来たと思います。

1次予選の結果は今月末です。本選に進めるか分かりませんが、参加して良かったなと感じています。

簡易Javaネットワークプログラミング

Javaでsocket機能を使用した非常に簡易なネットワークプログラムを作りました。
ありきたりですが、Clientから郵便番号をリクエストすると、Serverから対応する住所情報をレスポンスします。
郵便データは、日本郵便 郵便データダウンロードから事前取得し、Server起動時に読み込むような非常に簡易なプログラムです。

1. Server Code

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;

public class OriginalServer {

	public HashMap<String, String> map;
	private String file;

	public OriginalServer(String file) {
		this.file = file;
	}

	public void read() {

		try {
			File f = new File(file);
			byte[] b = new byte[(int) f.length()];
			FileInputStream fi = null;
			fi = new FileInputStream(f);
			fi.read(b);
			String str = new String(b);
			String[] strrec = str.split("n");

			System.out.println("To be prepared.....");

			String[] workArray;
			map = new HashMap<String, String>();
			for (int i = 0; i < strrec.length; i++) {
				workArray = strrec[i].split(",");
				map.put(workArray[2].replace(""", ""),
						workArray[6].replace(""", "")
								+ workArray[7].replace(""", "")
								+ workArray[8].replace(""", ""));
			}

			System.out.println("Wait for request.....");

		} catch (FileNotFoundException e) {
			System.out.println("File Not Found Error");
			System.exit(-1);
		} catch (IOException e) {
			System.out.println("File Read Error");
			System.exit(-1);
		}
	}

	public static void main(String args[]) {
		try {
			ServerSocket serverSocket = new ServerSocket(
					Integer.parseInt(args[0]));

			OriginalServer os = new OriginalServer(args[1]);

			// read address-File
			os.read();

			while (true) {
				Socket socket = serverSocket.accept();
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(socket.getInputStream()));

				String post;
				while ((post = reader.readLine()) != null) {
					// readline() + Empty() = Start HTTP-BODY
					if (post.isEmpty()) {
						post = reader.readLine().replace("-", "");
						break;
					}
				}
				String addr;
				if (os.map.containsKey(post)) {
					addr = "〒" + post.substring(0, 3) + "-"
							+ post.substring(3, post.length()) + " -> "
							+ os.map.get(post) + "n";
				} else {
					addr = "There is no postal code.n";
				}

				PrintStream writer = new PrintStream(socket.getOutputStream());

				writer.print("HTTP/1.1 200 OKn");
				writer.print("Connection: closen");
				writer.print("Content-Length: " + addr.length() + "n");
				writer.print("Content-Type:text/plainnn");
				writer.print(addr);
				writer.flush();
				writer.close();
				reader.close();
				socket.close();
			}
		} catch (SocketException e) {
			System.out.println("Socket Error");
			System.exit(-1);
		} catch (IOException e) {
			System.out.println("IO Error");
			System.exit(-1);
		}
	}
}

2. Client Code

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

public class OriginalClient {

	public static void main(String args[]) {
		try {

			Socket socket = new Socket(args[0], Integer.parseInt(args[1]));
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
					socket.getOutputStream()));

			writer.write("GET / HTTP/1.1rn");
			writer.write("Host: " + args[0] + ":" + args[1] + "rn");
			writer.write("Connection: closern");
			writer.write("rn");
			writer.write(args[2] + "rn");
			writer.flush();

			String line;
			while ((line = reader.readLine()) != null) {
				if (line.isEmpty()) {
					System.out.println(reader.readLine());
				}
			}

			writer.close();
			reader.close();
			socket.close();

		} catch (UnknownHostException e) {
			System.out.println("Host not found");
			System.exit(-1);
		} catch (SocketException e) {
		} catch (IOException e) {
			System.out.println("IO error");
			System.exit(-1);
		}
	}
}

3. 実行結果: Server

C:hogenetworkPog > java OriginalServer 12345 KEN_ALL.CSV
To be Prepared....
Wait for request....

4. 実行結果: Client

C:hogenetworkPog > java OriginalClient localhost 12345 1400011
〒140-0011 -> 東京都品川区東大井

以上です。

QRコード with ZXing

ZXing(”Zebra Crossing”)を使用したQRコード生成ツールを作成しました。
ZXingはGoogleがオープンソースで公開しているQRコード生成ライブラリです。
Androidでも使用されていますが、今回はローカル起動までとなります。
以下では、Antを使用してビルドしていますので、Antのセットアップをしておいてください。

1. ZXing セットアップ
まず、Google CodeからZXingをダウンロードします。
任意のディレクトリへ展開します。私は “C:Eclipse_programzxing-1.6” としました。
次に展開済みのフォルダ内にある READ MEファイルを開き、ビルド用のコマンドを確認します。

Please refer to the project page for more information:
http://code.google.com/p/zxing/
in particular:
http://code.google.com/p/zxing/wiki/GettingStarted

To get started, you can try building and running the command-line client;
you will need to have Apache's Ant tool installed to run this:

ant -f core/build.xml
ant -f javase/build.xml
java -cp javase/javase.jar:core/core.jar com.google.zxing.client.j2se.CommandLineRunner [URL]

コマンドプロンプトを開き、展開をしたディレクトリへ移動し、12,13行目のAntコマンドを実行します。

C:Eclipse_programzxing-1.6>ant -f core/build.xml
Buildfile: C:Eclipse_programzxing-1.6corebuild.xml

clean:
   [delete] Deleting directory C:Eclipse_programzxing-1.6corebuild
   [delete] Deleting: C:Eclipse_programzxing-1.6corecore.jar

build:

init:

compile:
    [mkdir] Created dir: C:Eclipse_programzxing-1.6corebuild
    [javac] C:Eclipse_programzxing-1.6corebuild.xml:36: warning: 'includeant
runtime' was not set, defaulting to build.sysclasspath=last; set to false for re
peatable builds
    [javac] Compiling 171 source files to C:Eclipse_programzxing-1.6corebuild
      [jar] Building jar: C:Eclipse_programzxing-1.6corecore.jar

BUILD SUCCESSFUL
Total time: 10 seconds
C:Eclipse_programzxing-1.6>ant -f javase/build.xml
Buildfile: C:Eclipse_programzxing-1.6javasebuild.xml

init:

build:
    [javac] C:Eclipse_programzxing-1.6javasebuild.xml:40: warning: 'includea
ntruntime' was not set, defaulting to build.sysclasspath=last; set to false for
repeatable builds

BUILD SUCCESSFUL
Total time: 0 seconds

最後にEclipseで使用する為に “C:Eclipse_programzxing-1.6corecore.jar” をライブラリ追加すればOKです。

2. 最終的なソースコード
何らかの文字列を入力すると、都度QRコードを生成するようになっています。
また、保存ボタン押下で生成済みのQRコードを保存します。
ちょっと怪しい部分もありますが、、ひとまず動くと思います。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;

public class Url2QR extends JFrame implements ActionListener {

	private static Url2QR frame;
	private final JTextField textField;
	private final JButton dispButton;
	private final JButton saveButton;
	private final JLabel label;
	private final JPanel panelTop;
	private final JPanel panelBottom;
	private final ImageIcon icon;
	private final BufferedImage bufImg;
	private static int SIZE = 4;

	public Url2QR() throws WriterException {
		super("QRコード生成");

		panelTop = new JPanel();
		panelBottom = new JPanel();

		textField = new JTextField(20);
		dispButton = new JButton("表示");
		saveButton = new JButton("保存");

		bufImg = barcodeWrite();
		icon = new ImageIcon(bufImg);
		label = new JLabel(icon);

		Container container = getContentPane();
		container.setLayout(new BorderLayout());

		container.add(textField, BorderLayout.CENTER);
		container.add(panelTop, BorderLayout.EAST);
		container.add(panelBottom, BorderLayout.SOUTH);
		panelTop.setLayout(new BorderLayout());
		panelTop.add(dispButton, BorderLayout.WEST);
		panelTop.add(saveButton, BorderLayout.EAST);
		panelBottom.setLayout(new BorderLayout());
		panelBottom.add(label, BorderLayout.WEST);

		dispButton.setActionCommand("display");
		dispButton.addActionListener(this);
		saveButton.setActionCommand("save");
		saveButton.addActionListener(this);
		saveButton.setEnabled(false);

		KeyListener keyListener = new KeyListener() {
			public void keyPressed(KeyEvent keyEvent) {
			}

			public void keyReleased(KeyEvent keyEvent) {
				repaintIcon();
			}

			public void keyTyped(KeyEvent keyEvent) {
			}
		};
		textField.addKeyListener(keyListener);
	}

	public void actionPerformed(ActionEvent e) {

		if (e.getActionCommand().equals("display")) {
			repaintIcon();
		} else if (e.getActionCommand().equals("save")) {
			saveQR();
		}

	}

	public void repaintIcon() {

		try {
			if (textField.getText().isEmpty()) {
				saveButton.setEnabled(false);
			} else {
				saveButton.setEnabled(true);
			}
			label.setIcon(new ImageIcon(barcodeWrite()));
		} catch (WriterException e) {
			e.printStackTrace();
		}
		label.setToolTipText(textField.getText());
		frame.pack();

	}

	public void saveQR() {

		JFileChooser filechooser = new JFileChooser();

		int selected = filechooser.showSaveDialog(this);
		if (selected == JFileChooser.APPROVE_OPTION) {
			File file = filechooser.getSelectedFile();
			try {
				ImageIO.write(barcodeWrite(), "png", file);
			} catch (IOException e) {
				System.out.println(e);
			} catch (WriterException e) {
				e.printStackTrace();
			}
		} else if (selected == JFileChooser.CANCEL_OPTION) {
		} else if (selected == JFileChooser.ERROR_OPTION) {
			label.setText("Error");
		}
	}

	public BufferedImage barcodeWrite() throws WriterException {

		Hashtable hints = new Hashtable();
		hints.put(EncodeHintType.CHARACTER_SET, "SHIFT_JIS");
		QRCode qrCode = new QRCode();
		Encoder.encode(textField.getText(), ErrorCorrectionLevel.L, hints,
				qrCode);
		ByteMatrix byteMatrix = qrCode.getMatrix();

		BufferedImage image = new BufferedImage(byteMatrix.getWidth() * SIZE,
				byteMatrix.getHeight() * SIZE, BufferedImage.TYPE_4BYTE_ABGR);

		if (!textField.getText().isEmpty()) {
			Graphics2D g2D = image.createGraphics();
			for (int y = 0; y < byteMatrix.getHeight(); y++) {
				for (int x = 0; x < byteMatrix.getWidth(); x++) {
					if (byteMatrix.get(x, y) == 1) {
						g2D.setColor(Color.black);
					} else {
						g2D.setColor(Color.white);
					}
					g2D.fillRect(x * SIZE, y * SIZE, SIZE, SIZE);
				}
			}
		}

		return image;

	}

	public static void startGUI() throws WriterException {

		frame = new Url2QR();
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
		frame.setVisible(true);

	}

	public static void main(String[] args) {

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					startGUI();
				} catch (WriterException e) {
					e.printStackTrace();
				}
			}
		});
	}

}

Antセットアップ

Mavenに続いて、Antも必要とのことだった為、セットアップしました。こちらはあっさり完了しました。

  1. Ant ダウンロード
    Apache Ant ProjectからAntをダウンロードします。
    Mavenと同様に解凍後、任意のディレクトリへ格納します。私は “C:Eclipse_programant-1.8.2” にしました。
  2. 環境変数設定
    次に環境変数を設定し、AntへPathを通します。(JAVAへもPathを通すこと)
    環境変数の設定が出来たら、コマンドプロンプトで確認します。

    C:Usershoge_user>ant -v
    Apache Ant(TM) version 1.8.2 compiled on December 20 2010
    Trying the default build file: build.xml
    Buildfile: build.xml does not exist!
    Build failed
    

    エラーが出ています。Antはビルドファイルの情報を元に、ビルドしますが、ファイル指定がなかった為にカレントディレクトリのbuild.xmlを探しにいきます。(そして、無かった)

  3. ビルドファイル作成
    テストの為にサンプルのbuild.xmlを任意のディレクトリに作成します。

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <project name="anttest" default="test">
    
      <target name="test">
        <echo message="Hello, World"/>
      </target>
    
    </project>
    

    ビルドファイルを作成したディレクトリに移動し、antを実行します。

    C:Eclipse_programant-1.8.2>ant
    Buildfile: C:Eclipse_programant-1.8.2build.xml
    
    test:
         [echo] Hello, World
    
    BUILD SUCCESSFUL
    Total time: 0 seconds
    

以上です。


Mavenセットアップ

大学院でMavenが必要になった為、Laptopに設定しました。
セットアップが完了したところで、以下のエラーメッセージがEclipseのコンソールに出ていて苦労したのですが、MAVEN→M2Eclipseの順番で入れ直すことで解消しました。  → が、また出た。。

Unable to update index for central|http://repo1.maven.org/maven2

以下の手順でセットアップします。

  1. Mavenダウンロード
    Apache Maven ProjectからMavenのzipファイルをダウンロードします。今回はMaven3.0.3にしました。
    zipファイルは解凍し、適当なディレクトリへ格納します。
    私は “C:Eclipse_programmaven-3.0.3” としました。
  2. 環境変数設定
    次に環境変数を設定し、MavenへPathを通します。当然、JavaへのPathも通しておいてください。
    環境変数の設定が出来たら、コマンドプロンプトで確認します。

    C:Usershoge_user>mvn -v
    Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900)
    Maven home: C:Eclipse_programmaven-3.0.3bin..
    Java version: 1.6.0_24, vendor: Sun Microsystems Inc.
    Java home: C:Program FilesJavajdk1.6.0_24jre
    Default locale: ja_JP, platform encoding: MS932
    OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"
    
  3. その他設定
  4. 設定ファイル”C:Eclipse_programmaven-3.0.3confsetting.xml” を編集します。
    ■Repositoryを適当に変更します。デフォルトだと下記のコメント内に出来ます。(初回起動時に勝手に作られる)

      <!-- localRepository
       | The path to the local repository maven will use to store artifacts.
       |
       | Default: ~/.m2/repository
      -->
      <localRepository>C:Eclipse_programmaven2Repository</localRepository>
    

    ■proxyを使っている場合はここをいじります。冒頭で記載したエラー文言でググると先頭の”proxies”からコメントアウトしろと書いてありましたが、特に変わらなかったです。一応、エラーが出たらココを確認してみてください。

    <proxies>
        <!-- proxy
         | Specification for one proxy, to be used in connecting to the network.
         |
        <proxy>
          <id>optional</id>
          <active>true</active>
          <protocol>http</protocol>
          <username>proxyuser</username>
          <password>proxypass</password>
          <host>proxy.host.net</host>
          <port>80</port>
          <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
        </proxy>
        -->
      </proxies>
    

    では、Eclipseにプラグインを追加する前に単体での動きを確認してみます。
    なお、こちらのサイトを参考にさせてもらいました。

    mvn archetype:create -DgroupId=jp.co.hogehoge -DartifactId=maventest
    

    初回実行時は複数のコンポーネントをダウンロードして、先に定義変更したLocal Repositoryに格納されます。
    また、C:UsersIhoge_usermaventest が出来ているので、change directryしてください。

    続いてビルドします。実行後、maventest配下にtargetディレクトリが出来て、classファイルも確認できます。

    mvn compile
  5. Eclipseにプラグインをインストール
    Eclipse上でM2Eclipseのプラグインをインストールします。
    [ヘルプ]-[新規ソフトウェアのインストール]で “http://m2eclipse.sonatype.org/sites/m2e” を登録し、プラグインをインストールします。インストールが完了したら、新規プロジェクトでMavenを選択し、確認してみてください。

と、書いていた矢先に、またエラー[Unable to update index for central]が・・・
もしかして、、作業していたネットワークの設定だけだったかもしれない。。



EclipseのJavadoc日本語化

EclipseのJavadocを日本語化しました。
EclipseのデフォルトJavadocは、Web上の公式ドキュメント(英語版)を参照するようになっています。
そのままでも良いのですが、ネットワークに繋がっていなくてもドキュメント参照出来るようにローカル側に配置しなおし、せっかくなので日本語版にしました。

オラクルのダウンロードサイトからJava SE 6ドキュメントをダウンロードします。
Zipファイルがダウンロード出来たら、任意の場所へ配置します。(解凍してもしなくてもどちらでも良いです)
私はEclipseのアプリケーションディレクトリ直下にいれてしまいました。

次に、Eclipseでパッケージエクスプローラを開きます。表示されていない場合は、
「ウィンドウ」-「ビューの表示」から選択します。
適当なプロジェクトを選択して、「JREシステム・ライブラリ」内のrt.jarのプロパティを開きます。
開いたらJavadocロケーションを選択し、アーカイブ内のJavadocオプションを選択→外部ファイルで
先ほどダウンロードしたファイル(ZIPのままでもOK)を指定します。
アーカイブ内のファイルパス項目はdocs/ja/api 等のapiまでのパスを指定します。
これで作業は完了なので、適当なクラス名等を打ち込んで、Javadocを呼び出します。(SHIFT+F2)

作業効率が少しは上がるかもしれません。。

Eclipseコンソール文字化け

Eclipseのコンソール上で日本語などのDBCSが文字化けしていたので、文字コードをUTF-8に統一しました。

Eclipseのデフォルトエンコードは”MS932″になっています。
確認は、Eclipseのメニューから「実行構成」を開き、「共通タブ」を開きます。
エンコードのセグメントにデフォルトエンコード”MS932″が指定されていることが確認出来ます。
ここで、他のエンコードを指定しなおして見たのですが、文字化けは変わりませんでした。

この指定はクラス毎に行っているようですので、そもそもEclipseのデフォルトエンコードを変更するようにしました。
Eclipseのアプリケーションディレクトリ内のeclipse.iniがあるので、”-Dfile.encoding=utf-8″を追加してEclipseを再起動すれば完了です。

-Xms40m
-Xmx384m
-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
-Dfile.encoding=utf-8

なお、-javaagent:plugins…..の部分は日本語化した際の追記行となります。



JAVAを始めました(今更)

猛烈に今更感がありますが、、JAVAを勉強し始めました。
新卒の時に勉強していて結局使用することなく、他の言語に移ってしまっていました。
今思うと、何故もっとやっておかなかったの・・・と考えてしまいますが、、人に言うのも恥ずかしさがあります。
自社製品はJAVAも多いですし、今後のAndroidアプリ開発時にも使えそうですので、ちょこちょこやってみようと思います。
また、新しい言語を覚える事は新たな発見がたくさんあり、他言語で不明瞭な点がくっきりするかもしれません。

久しぶりに使うEclipseは高機能なこともあり使用方法に戸惑っています。
一番面倒なのはデフォルトではエディタがvi風に使用出来なく、特に移動などが面倒でしたので、ViLikeを投入してみました。
まだ少ししか触っていないですが、これは良いです。
当初、viPluginを入れようとしたのですが、有料版になってしまったようです。

Eclipse 3.6 完全攻略を買ったので、まずは色々さわってみよう。