The next JAVA library(10)

さて、open()とclose()の実装です。
open()の場合は、jarファイルに含まれているリソースか、スクラッチパッドorレコードストアをbyte配列に格納して、その時点で、開いていたファイルはcloseします。
close()は、byte配列を、open()の時に開いたファイルを再び開いて、書き込みます。
ただし、1度も編集していない場合は不要なので、書き込みません。
また、jarファイルに含まれているリソースはread onlyなので、これも書き込みません。
かなり長くなりますが、次のようになります。

protected byte stream;
protected int streamOffset;

//openしたときのファイル名
protected String fileName;

//1度でも書き込んだかどうか
private boolean bWrite;
//jarファイルに含まれているリソースかどうか
private boolean bInputStream;

private byte b4 = new byte[4];

//ファイルサイズ計測用のバッファ
private byte sizeBuf = new byte[1024];

//DoCoMo用の処理
#if DOCOMO

private int scratchOffset;
protected boolean _open(String fileName){
    try{
        if(stream != null){
            throw new IOException("file is already opened.");
        }
        InputStream is;
        int size = 0;
        this.fileName = null;
        is = getClass().getResourceAsStream(fileName);
        if(is != null){
            //ファイルはjarファイルの中にあった
            int n;
            //ファイルサイズを計測
            while)((n = is.read(sizeBuf))( >= 0){
                size += n;
            }
            is.close();
            is = getClass().getResourceAsStream(fileName);
            bInputStream = true;
        }else{
            //スクラッチパッドから検索
            is = Connector.openInputStream("scratchpad:///0");
            scratchOffset = 0;
            int n;
            while(true){
                n = is.read();
                if(n <= 0){
                    //ファイルが見つからなかった
                    is.close();
                    return false;
                }
                byte b = new byte[n];
                is.read(b);
                scratchOffset += 1+n;
                String s = new String(b);
                if(fileName.compareTo(s.toString()) == 0){
                    //ファイルがあった
                    is.read(b4);
                    //ファイルサイズを取得
                    size = byteToInt(b4,0,4);
                    scratchOffset += 4;
                    bInputStream = false;
                    break;
                }else{
                    //探していたファイルではなかった
                    is.read(b4);
                    scratchOffset += is.skip(byteToInt(b4,0,4))+4;
                }
            }
        }
        //ファイルサイズ分のbyte配列に、データを入れる
        stream = new byte[size];
        is.read(stream);
        is.close();
        streamOffset = 0;
        this.fileName = fileName;
        bWrite = false;
        return true;
    }catch(Exception e){
        System.out.println("Open:"+e);
    }
    return false;
}

protected void _close(){
    if(stream == null) return;
    try{
        if(!bInputStream && bWrite){
            //jarファイル内のリソースではなくて、
            //一回でも書き込みしている場合は
            //データを全てflushする
            OutputStream os = Connector.openOutputStream(
                "scratchpad:///0;pos="+scratchOffset);
            os.write(stream);
            os.close();
        }
        stream = null;
    }catch(Exception e){
        System.out.println("Close:"+e);
    }
}

//MIDP用の処理
#else

protected boolean _open(String fileName){
    try{
        if(stream != null){
            throw new IOException("file is already opened.");
        }
        InputStream is;
        int size = 0;
        this.fileName = null;
        is = getClass().getResourceAsStream(fileName);
        if(is != null){
            //ファイルはjarファイルの中にあった
            int n;
            //ファイルサイズを計測
            while((n = is.read(sizeBuf)) >= 0){
                size += n;
            }
            is.close();
            is = getClass().getResourceAsStream(fileName);
            stream = new byte[size];
            is.read(stream);
            is.close();
            bInputStream = true;
        }else{
            //レコードストアの場合、getRecord()でbyte配列を取得できる
            RecordStore rs =
                RecordStore.openRecordStore(fileName, false);
            stream = rs.getRecord(1);
            rs.closeRecordStore();
            bInputStream = false;
        }
        streamOffset = 0;
        this.fileName = fileName;
        bWrite = false;
        return true;
    }catch(Exception e){
        System.out.println("Open:"+e);
    }
    return false;
}

protected void _close(){
    if(stream == null) return;
    try{
        if(!bInputStream && bWrite){
            //jarファイル内のリソースではなくて、
            //一回でも書き込みしている場合は
            //データを全てflushする
            RecordStore rs =
                RecordStore.openRecordStore(fileName, false);
            rs.setRecord(1, stream, 0, stream.length);
            rs.closeRecordStore();
        }
        stream = null;
    }catch(Exception e){
        System.out.println("Close:"+e);
    }
}

#endif

長すぎですね。すんません(;´Д`)
ファイルサイズ的にも少し厳しいですが、read()やwrite()等の、他のメソッドが軽いので大丈夫でしょう。多分……(;´Д`)
クラッチパッドのファイル形式は、The next JAVA library(7)で書いた形式になっています。
蛇足になりますけど、スクラッチパッドですが、画像データなどの大きいデータをスクラッチパッドに入れると、skipするだけでかなりの時間を取ります。
なので、もしDoJa3.0以降しか使わなくて、ファイルを16個より多く作らない、という仕様にするのであれば、分割したスクラッチパッドを、1つのファイルとして割り当てるのもありだと思います。
また、スクラッチパッドの0番目をindexとして、そこから目的の場所を開く、というのもいいかもしれません。
あと、jarファイルからのリソースのサイズを取得するために空読みさせるのにも、機種によっては時間がかかるかもしれません(実験したことがないのでわかりませんが)。
そういうときは、リソースの中に、それぞれのファイルサイズを記したファイルを一つ用意して、それを読み込んで使用するようにすればいいですね。
まあ、この辺は適時組み替えていくことにしましょう。
入力と出力さえ同じであれば、内部ロジックはいつでも変更できますからねヽ(´ー`)ノ


これでファイル操作は終わりです。長かったなぁ(;´Д`)
次はサウンド関連に入りたいですが、まだその辺は1行も書いてないのでしばらく時間が空きます。
でも、サウンドってバリバリ機種依存するからあんまり好きじゃないんだよなぁ(;´Д`)
むしろ、無理矢理1つの仕様にするのではなく、サウンドの部分だけは、機種に応じて書き換えても良いんじゃないか、と思ってしまいますね。
まあ、どうしても変になるようだったら、実装するのは諦めておきます。
……このライブラリ、使えるようになるのはいつになるやら(;´Д`)


追記:
ふと思ったのですが、これって、

protected boolean _open(byte buf){
    if(stream != null) return false;
    if(buf == null) return false;
    stream = buf;
    return true;
}

protected byte _getBuffer(){
    return stream;
}

こうやって使えば、ByteArrayInputStreamとByteArrayOutputStreamを統合して、さらに機能を付与した、配列用入出力ストリームになりますねヽ(´ー`)ノ
まあ、streamが1つしか存在していないので、同時に操作できるbyte配列が1つなのが難点ですが。
こういうときに、クラス化できないのが不満に思いますね。携帯JAVAって、OOP?ナニソレ?ってな感じですからね(;´Д`)
この鬱憤はBREWのライブラリを作るときに発散させることにします(`ヘ´)