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