FileDocCategorySizeDatePackage
QueuedHeader.javaAPI DocphoneME MR2 API (J2ME)12436Wed May 02 18:00:32 BST 2007com.sun.kvem.jsr082.obex

QueuedHeader.java

/*
 *   
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.kvem.jsr082.obex;

import java.util.Calendar;
import java.util.Vector;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class QueuedHeader {

    /** Debug information, should be false for RR. */
    private static final boolean DEBUG = false;

    private ObexPacketStream stream;
    private int type;
    private Object value;
    byte[] buffer;
    int packetLength;

    QueuedHeader(ObexPacketStream stream) {
        this.stream = stream;
    }

    /**
     *
     * @param type
     * @param value
     * @throws IOException
     */
    void sendOrQueue(int type, Object value) throws IOException {
        if (stream.moreHeaders && send(type, value)) {
            stream.challengesToSend = true;
            return;
        }
        queue(type, value);
    }

    /**
     * Adds the header to queue in ObexPacketStream.
     * @param type header's type defined in <code>HeaderSetImpl<code> class.
     * @param value header's value.
     * @throws IOException thrown if the header is too large.
     */
    void queue(int type, Object value) throws IOException {
        int maxSendLength = stream.maxSendLength;
        boolean fail = false;

        switch (HeaderSetImpl.internalType(type)) {
            // ids which require special handling
            case HeaderSetImpl.TYPE_SPECIAL_TIME_ISO:
            case HeaderSetImpl.TYPE_SPECIAL_TIME_4:
            case HeaderSetImpl.TYPE_LONG:
            case HeaderSetImpl.TYPE_BYTE:
                break;

            case HeaderSetImpl.TYPE_SPECIAL_TYPE:
                // type8 + len16 + head + len16 + bytes[length] + zero8
                if (((String)value).length() + 7 > maxSendLength) {
                    fail = true;
                }
                break;

            case HeaderSetImpl.TYPE_UNICODE:
                // type8 + len16 + head + len16 + bytes[length] + zero16
                if ((((String)value).length() << 1) + 8 > maxSendLength) {
                    fail = true;
                }
                break;

            case HeaderSetImpl.TYPE_BYTEARRAY:
                // type8 + len16 + head + len16 + bytes[length]
                if (((byte[])value).length + 6 > maxSendLength) {
                    fail = true;
                }
                break;

            case HeaderSetImpl.TYPE_AUTH_CHALLENGE:
                Vector challenges = (Vector)value;
                if (challenges.isEmpty())
		    return;
                int len = 0;
                for (int i = 0; i < challenges.size(); i++) {
                    len += ((ObexAuth) challenges.elementAt(i))
                        .prepareChallenge();
                }
                if (len + 3 > maxSendLength) {
                    fail = true;
                }
                break;
        }

        if (fail) {
            stream.headerTooLarge();
            return;
        }
        this.type = type;
        this.value = value;
        newHeader();
    }

    boolean trySendAgain() throws IOException {
        return send(type, value);
    }

    boolean send(int type, Object value) throws IOException {
        buffer = stream.buffer;
        packetLength = stream.packetLength;
        int maxSendLength = stream.maxSendLength;
        if (DEBUG) {
            System.out.println("Sending header 0x" + Integer.toHexString(type));
        }

        try {
            buffer[packetLength++] = (byte)type;
            switch (HeaderSetImpl.internalType(type)) {
                // ids which require special handling
                case HeaderSetImpl.TYPE_SPECIAL_TIME_ISO:
                    encodeTime8601((Calendar)value);
                    break;

                case HeaderSetImpl.TYPE_SPECIAL_TIME_4:
                    encodeInt(((Calendar)value).getTime().getTime() / 1000L);
                    break;

                case HeaderSetImpl.TYPE_SPECIAL_TYPE:
                    byte[] str = ((String)value).getBytes("ISO-8859-1");

                    // head + len16 + bytes[length] + zero8
                    encodeLength16(str.length + 4);
                    System.arraycopy(str, 0, buffer, packetLength,
                            str.length);
                    packetLength += str.length;
                    // null terminator required by protocol spec
                    buffer[packetLength++] = 0;
                    break;

                case HeaderSetImpl.TYPE_AUTH_CHALLENGE:
                    packetLength--;
                    Vector challenges = (Vector) value;

                    stream.authFailed = false;

                    for (int i = 0; i < challenges.size(); i++) {
                        ObexAuth auth = (ObexAuth) challenges.elementAt(i);
                        packetLength += auth.addChallenge(buffer, packetLength);
                    }

                    // need to be shure - challenges are added
                    if (packetLength > maxSendLength) {
                        break;
                    }

                    // if we still there, then authChallenges
		    // successfully added
                    for (int i = 0; i < challenges.size(); i++) {
                        stream.authFailed = true;
                        stream.authChallenges.addElement(
                                challenges.elementAt(i));
                    }
                    break;

                    // normal ids

                    // 4 byte integer values, range (0xC0 - 0xFF)
                    // system and user defined
                case HeaderSetImpl.TYPE_LONG:
                    encodeInt(((Long)value).longValue());
                    break;

                    // unicode encoded string values, range (0x00 - 0x3F)
                    // system and user defined
                case HeaderSetImpl.TYPE_UNICODE:
                    str = ((String)value).getBytes("UTF-16BE");
                    // str = ((String)value).getBytes("ISO-8859-1");

                    // head + len16 + bytes[length] + zero16
                    encodeLength16(str.length + 5);
                    System.arraycopy(str, 0, buffer, packetLength,
                            str.length);
                    packetLength += str.length;
                    // end of unicode string
                    buffer[packetLength++] = 0;
                    buffer[packetLength++] = 0;
                    break;

                    // byte[] array values, range (0x40 - 0x7F)
                    // system and user defined
                case HeaderSetImpl.TYPE_BYTEARRAY:
                    byte[] array = (byte[]) value;

                    // head + len16 + bytes[length]
                    encodeLength16(array.length + 3);
                    System.arraycopy(array, 0, buffer, packetLength,
                            array.length);
                    packetLength += array.length;
                    stream.containsTargetHeader |=
                        (type == HeaderSetImpl.TARGET);
                    break;

                    // byte values, range (0x80 - 0xBF)
                    // user defined
                case HeaderSetImpl.TYPE_BYTE:
                    buffer[packetLength++] =
                        (byte)((Byte)value).byteValue();
                    break;

                    // case HeaderSetImpl.TYPE_UNSUPPORTED:
                default:
                    if (DEBUG) {
                        System.out.println("wrong or unsupported header");
                    }
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.toString());
        } catch (ArrayIndexOutOfBoundsException e) {
            stream.moreHeaders = false;
            return false;
        }
        if (packetLength > maxSendLength) {
            stream.moreHeaders = false;
            return false;
        }

        // finally closing body headers if opened
        if (stream.dataOpened) {
            int dataOffset = stream.dataOffset;
            int len = stream.packetLength - dataOffset;
            buffer[dataOffset+1] = (byte)(len >> 8);
            buffer[dataOffset+2] = (byte)len;
            stream.dataOpened = false;
            stream.dataClosed = true;
        }
        stream.packetLength = packetLength;

        // gc header value
        this.value = null;
        if (DEBUG) {
            System.out.println("  -> sent!");
        }
        return true;
    }

    private void newHeader() {
        stream.queuedHeaders.addElement(this);
        if (stream.emptyHeadersPool.empty()) {
            stream.newHeader = new QueuedHeader(stream);
        } else {
            stream.newHeader = (QueuedHeader) stream.emptyHeadersPool.pop();
        }
    }

    boolean sendAllQueued() throws IOException {
        while (stream.queuedHeaders.size() > 0 && stream.moreHeaders) {
            QueuedHeader header = (QueuedHeader)
                stream.queuedHeaders.firstElement();
            boolean res = header.trySendAgain();
            if (!res) {
                return false;
            }
            stream.queuedHeaders.removeElementAt(0);
        }
        return true;
    }

    private final void encodeInt(long val) {
        buffer[packetLength++] = (byte)(val >> 24);
        buffer[packetLength++] = (byte)(val >> 16);
        buffer[packetLength++] = (byte)(val >> 8);
        buffer[packetLength++] = (byte)val;
    }

    private final void encodeLength16(int value) {
        buffer[packetLength++] = (byte)(value >> 8);
        buffer[packetLength++] = (byte)value;
    }

    private final void encodeTime8601(Calendar cal) {
        encodeLength16(19);

        // copy calendar as it can be with wrong timezone.
        Calendar cal2 = Calendar.getInstance(ObexPacketStream.utcTimeZone);
        cal2.setTime(cal.getTime());
        int year = cal2.get(Calendar.YEAR);
        int month = cal2.get(Calendar.MONTH) + 1; // Calendar.JANUARY = 0
        int date = cal2.get(Calendar.DATE);
        int hour = cal2.get(Calendar.HOUR_OF_DAY);
        int minute = cal2.get(Calendar.MINUTE);
        int second = cal2.get(Calendar.SECOND);
        int zero = 0x30; // zero ('0') code in latin1

        if (year < 0 || year > 9999) {
            if (DEBUG) {
                System.out.println("wrong date header");
            }
        }
        buffer[packetLength++] = (byte)(year / 1000 + zero);
        year = year % 1000;
        buffer[packetLength++] = (byte)(year / 100 + zero);
        year = year % 100;
        buffer[packetLength++] = (byte)(year / 10 + zero);
        year = year % 10;
        buffer[packetLength++] = (byte)(year + zero);
        buffer[packetLength++] = (byte)(month / 10 + zero);
        buffer[packetLength++] = (byte)(month % 10 + zero);
        buffer[packetLength++] = (byte)(date / 10 + zero);
        buffer[packetLength++] = (byte)(date % 10 + zero);
        buffer[packetLength++] = 0x54; // 'T' code in latin1
        buffer[packetLength++] = (byte)(hour / 10 + zero);
        buffer[packetLength++] = (byte)(hour % 10 + zero);
        buffer[packetLength++] = (byte)(minute / 10 + zero);
        buffer[packetLength++] = (byte)(minute % 10 + zero);
        buffer[packetLength++] = (byte)(second / 10 + zero);
        buffer[packetLength++] = (byte)(second % 10 + zero);
        buffer[packetLength++] = (byte)0x5A; // 'Z' code in latin1
    }
}