1/*
2 * Copyright (C) 2007-2008 Esmertec AG.
3 * Copyright (C) 2007-2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.google.android.mms.pdu;
19
20import android.util.Config;
21import android.util.Log;
22
23import java.io.ByteArrayOutputStream;
24import java.io.IOException;
25import java.io.UnsupportedEncodingException;
26import java.util.ArrayList;
27
28/**
29 * Encoded-string-value = Text-string | Value-length Char-set Text-string
30 */
31public class EncodedStringValue implements Cloneable {
32    private static final String TAG = "EncodedStringValue";
33    private static final boolean DEBUG = false;
34    private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
35
36    /**
37     * The Char-set value.
38     */
39    private int mCharacterSet;
40
41    /**
42     * The Text-string value.
43     */
44    private byte[] mData;
45
46    /**
47     * Constructor.
48     *
49     * @param charset the Char-set value
50     * @param data the Text-string value
51     * @throws NullPointerException if Text-string value is null.
52     */
53    public EncodedStringValue(int charset, byte[] data) {
54        // TODO: CharSet needs to be validated against MIBEnum.
55        if(null == data) {
56            throw new NullPointerException("EncodedStringValue: Text-string is null.");
57        }
58
59        mCharacterSet = charset;
60        mData = new byte[data.length];
61        System.arraycopy(data, 0, mData, 0, data.length);
62    }
63
64    /**
65     * Constructor.
66     *
67     * @param data the Text-string value
68     * @throws NullPointerException if Text-string value is null.
69     */
70    public EncodedStringValue(byte[] data) {
71        this(CharacterSets.DEFAULT_CHARSET, data);
72    }
73
74    public EncodedStringValue(String data) {
75        try {
76            mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
77            mCharacterSet = CharacterSets.DEFAULT_CHARSET;
78        } catch (UnsupportedEncodingException e) {
79            Log.e(TAG, "Default encoding must be supported.", e);
80        }
81    }
82
83    /**
84     * Get Char-set value.
85     *
86     * @return the value
87     */
88    public int getCharacterSet() {
89        return mCharacterSet;
90    }
91
92    /**
93     * Set Char-set value.
94     *
95     * @param charset the Char-set value
96     */
97    public void setCharacterSet(int charset) {
98        // TODO: CharSet needs to be validated against MIBEnum.
99        mCharacterSet = charset;
100    }
101
102    /**
103     * Get Text-string value.
104     *
105     * @return the value
106     */
107    public byte[] getTextString() {
108        byte[] byteArray = new byte[mData.length];
109
110        System.arraycopy(mData, 0, byteArray, 0, mData.length);
111        return byteArray;
112    }
113
114    /**
115     * Set Text-string value.
116     *
117     * @param textString the Text-string value
118     * @throws NullPointerException if Text-string value is null.
119     */
120    public void setTextString(byte[] textString) {
121        if(null == textString) {
122            throw new NullPointerException("EncodedStringValue: Text-string is null.");
123        }
124
125        mData = new byte[textString.length];
126        System.arraycopy(textString, 0, mData, 0, textString.length);
127    }
128
129    /**
130     * Convert this object to a {@link java.lang.String}. If the encoding of
131     * the EncodedStringValue is null or unsupported, it will be
132     * treated as iso-8859-1 encoding.
133     *
134     * @return The decoded String.
135     */
136    public String getString()  {
137        if (CharacterSets.ANY_CHARSET == mCharacterSet) {
138            return new String(mData); // system default encoding.
139        } else {
140            try {
141                String name = CharacterSets.getMimeName(mCharacterSet);
142                return new String(mData, name);
143            } catch (UnsupportedEncodingException e) {
144            	if (LOCAL_LOGV) {
145            		Log.v(TAG, e.getMessage(), e);
146            	}
147            	try {
148                    return new String(mData, CharacterSets.MIMENAME_ISO_8859_1);
149                } catch (UnsupportedEncodingException _) {
150                    return new String(mData); // system default encoding.
151                }
152            }
153        }
154    }
155
156    /**
157     * Append to Text-string.
158     *
159     * @param textString the textString to append
160     * @throws NullPointerException if the text String is null
161     *                      or an IOException occured.
162     */
163    public void appendTextString(byte[] textString) {
164        if(null == textString) {
165            throw new NullPointerException("Text-string is null.");
166        }
167
168        if(null == mData) {
169            mData = new byte[textString.length];
170            System.arraycopy(textString, 0, mData, 0, textString.length);
171        } else {
172            ByteArrayOutputStream newTextString = new ByteArrayOutputStream();
173            try {
174                newTextString.write(mData);
175                newTextString.write(textString);
176            } catch (IOException e) {
177                e.printStackTrace();
178                throw new NullPointerException(
179                        "appendTextString: failed when write a new Text-string");
180            }
181
182            mData = newTextString.toByteArray();
183        }
184    }
185
186    /*
187     * (non-Javadoc)
188     * @see java.lang.Object#clone()
189     */
190    @Override
191    public Object clone() throws CloneNotSupportedException {
192        super.clone();
193        int len = mData.length;
194        byte[] dstBytes = new byte[len];
195        System.arraycopy(mData, 0, dstBytes, 0, len);
196
197        try {
198            return new EncodedStringValue(mCharacterSet, dstBytes);
199        } catch (Exception e) {
200            Log.e(TAG, "failed to clone an EncodedStringValue: " + this);
201            e.printStackTrace();
202            throw new CloneNotSupportedException(e.getMessage());
203        }
204    }
205
206    /**
207     * Split this encoded string around matches of the given pattern.
208     *
209     * @param pattern the delimiting pattern
210     * @return the array of encoded strings computed by splitting this encoded
211     *         string around matches of the given pattern
212     */
213    public EncodedStringValue[] split(String pattern) {
214        String[] temp = getString().split(pattern);
215        EncodedStringValue[] ret = new EncodedStringValue[temp.length];
216        for (int i = 0; i < ret.length; ++i) {
217            try {
218                ret[i] = new EncodedStringValue(mCharacterSet,
219                        temp[i].getBytes());
220            } catch (NullPointerException _) {
221                // Can't arrive here
222                return null;
223            }
224        }
225        return ret;
226    }
227
228    /**
229     * Extract an EncodedStringValue[] from a given String.
230     */
231    public static EncodedStringValue[] extract(String src) {
232        String[] values = src.split(";");
233
234        ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
235        for (int i = 0; i < values.length; i++) {
236            if (values[i].length() > 0) {
237                list.add(new EncodedStringValue(values[i]));
238            }
239        }
240
241        int len = list.size();
242        if (len > 0) {
243            return list.toArray(new EncodedStringValue[len]);
244        } else {
245            return null;
246        }
247    }
248
249    /**
250     * Concatenate an EncodedStringValue[] into a single String.
251     */
252    public static String concat(EncodedStringValue[] addr) {
253        StringBuilder sb = new StringBuilder();
254        int maxIndex = addr.length - 1;
255        for (int i = 0; i <= maxIndex; i++) {
256            sb.append(addr[i].getString());
257            if (i < maxIndex) {
258                sb.append(";");
259            }
260        }
261
262        return sb.toString();
263    }
264
265    public static EncodedStringValue copy(EncodedStringValue value) {
266        if (value == null) {
267            return null;
268        }
269
270        return new EncodedStringValue(value.mCharacterSet, value.mData);
271    }
272
273    public static EncodedStringValue[] encodeStrings(String[] array) {
274        int count = array.length;
275        if (count > 0) {
276            EncodedStringValue[] encodedArray = new EncodedStringValue[count];
277            for (int i = 0; i < count; i++) {
278                encodedArray[i] = new EncodedStringValue(array[i]);
279            }
280            return encodedArray;
281        }
282        return null;
283    }
284}
285