diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/java/com/android/common/ArrayListCursor.java | 172 | ||||
-rw-r--r-- | common/java/com/android/common/FastXmlSerializer.java | 365 | ||||
-rw-r--r-- | common/java/com/android/common/NetworkConnectivityListener.java | 224 | ||||
-rw-r--r-- | common/java/com/android/common/XmlUtils.java | 796 |
4 files changed, 1557 insertions, 0 deletions
diff --git a/common/java/com/android/common/ArrayListCursor.java b/common/java/com/android/common/ArrayListCursor.java new file mode 100644 index 0000000..cc1fe27 --- /dev/null +++ b/common/java/com/android/common/ArrayListCursor.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.common; + +import android.database.AbstractCursor; +import android.database.CursorWindow; + +import java.lang.System; +import java.util.ArrayList; + +/** + * A convenience class that presents a two-dimensional ArrayList + * as a Cursor. + * @deprecated This is has been replaced by MatrixCursor. +*/ +public class ArrayListCursor extends AbstractCursor { + private String[] mColumnNames; + private ArrayList<Object>[] mRows; + + @SuppressWarnings({"unchecked"}) + public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) { + int colCount = columnNames.length; + boolean foundID = false; + // Add an _id column if not in columnNames + for (int i = 0; i < colCount; ++i) { + if (columnNames[i].compareToIgnoreCase("_id") == 0) { + mColumnNames = columnNames; + foundID = true; + break; + } + } + + if (!foundID) { + mColumnNames = new String[colCount + 1]; + System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length); + mColumnNames[colCount] = "_id"; + } + + int rowCount = rows.size(); + mRows = new ArrayList[rowCount]; + + for (int i = 0; i < rowCount; ++i) { + mRows[i] = rows.get(i); + if (!foundID) { + mRows[i].add(i); + } + } + } + + @Override + public void fillWindow(int position, CursorWindow window) { + if (position < 0 || position > getCount()) { + return; + } + + window.acquireReference(); + try { + int oldpos = mPos; + mPos = position - 1; + window.clear(); + window.setStartPosition(position); + int columnNum = getColumnCount(); + window.setNumColumns(columnNum); + while (moveToNext() && window.allocRow()) { + for (int i = 0; i < columnNum; i++) { + final Object data = mRows[mPos].get(i); + if (data != null) { + if (data instanceof byte[]) { + byte[] field = (byte[]) data; + if (!window.putBlob(field, mPos, i)) { + window.freeLastRow(); + break; + } + } else { + String field = data.toString(); + if (!window.putString(field, mPos, i)) { + window.freeLastRow(); + break; + } + } + } else { + if (!window.putNull(mPos, i)) { + window.freeLastRow(); + break; + } + } + } + } + + mPos = oldpos; + } catch (IllegalStateException e){ + // simply ignore it + } finally { + window.releaseReference(); + } + } + + @Override + public int getCount() { + return mRows.length; + } + + @Override + public boolean deleteRow() { + return false; + } + + @Override + public String[] getColumnNames() { + return mColumnNames; + } + + @Override + public byte[] getBlob(int columnIndex) { + return (byte[]) mRows[mPos].get(columnIndex); + } + + @Override + public String getString(int columnIndex) { + Object cell = mRows[mPos].get(columnIndex); + return (cell == null) ? null : cell.toString(); + } + + @Override + public short getShort(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.shortValue(); + } + + @Override + public int getInt(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.intValue(); + } + + @Override + public long getLong(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.longValue(); + } + + @Override + public float getFloat(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.floatValue(); + } + + @Override + public double getDouble(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.doubleValue(); + } + + @Override + public boolean isNull(int columnIndex) { + return mRows[mPos].get(columnIndex) == null; + } +} diff --git a/common/java/com/android/common/FastXmlSerializer.java b/common/java/com/android/common/FastXmlSerializer.java new file mode 100644 index 0000000..0d33941 --- /dev/null +++ b/common/java/com/android/common/FastXmlSerializer.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.common; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; + +/** + * This is a quick and dirty implementation of XmlSerializer that isn't horribly + * painfully slow like the normal one. It only does what is needed for the + * specific XML files being written with it. + */ +public class FastXmlSerializer implements XmlSerializer { + private static final String ESCAPE_TABLE[] = new String[] { + null, null, null, null, null, null, null, null, // 0-7 + null, null, null, null, null, null, null, null, // 8-15 + null, null, null, null, null, null, null, null, // 16-23 + null, null, null, null, null, null, null, null, // 24-31 + null, null, """, null, null, null, "&", null, // 32-39 + null, null, null, null, null, null, null, null, // 40-47 + null, null, null, null, null, null, null, null, // 48-55 + null, null, null, null, "<", null, ">", null, // 56-63 + }; + + private static final int BUFFER_LEN = 8192; + + private final char[] mText = new char[BUFFER_LEN]; + private int mPos; + + private Writer mWriter; + + private OutputStream mOutputStream; + private CharsetEncoder mCharset; + private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); + + private boolean mInTag; + + private void append(char c) throws IOException { + int pos = mPos; + if (pos >= (BUFFER_LEN-1)) { + flush(); + pos = mPos; + } + mText[pos] = c; + mPos = pos+1; + } + + private void append(String str, int i, final int length) throws IOException { + if (length > BUFFER_LEN) { + final int end = i + length; + while (i < end) { + int next = i + BUFFER_LEN; + append(str, i, next<end ? BUFFER_LEN : (end-i)); + i = next; + } + return; + } + int pos = mPos; + if ((pos+length) > BUFFER_LEN) { + flush(); + pos = mPos; + } + str.getChars(i, i+length, mText, pos); + mPos = pos + length; + } + + private void append(char[] buf, int i, final int length) throws IOException { + if (length > BUFFER_LEN) { + final int end = i + length; + while (i < end) { + int next = i + BUFFER_LEN; + append(buf, i, next<end ? BUFFER_LEN : (end-i)); + i = next; + } + return; + } + int pos = mPos; + if ((pos+length) > BUFFER_LEN) { + flush(); + pos = mPos; + } + System.arraycopy(buf, i, mText, pos, length); + mPos = pos + length; + } + + private void append(String str) throws IOException { + append(str, 0, str.length()); + } + + private void escapeAndAppendString(final String string) throws IOException { + final int N = string.length(); + final char NE = (char)ESCAPE_TABLE.length; + final String[] escapes = ESCAPE_TABLE; + int lastPos = 0; + int pos; + for (pos=0; pos<N; pos++) { + char c = string.charAt(pos); + if (c >= NE) continue; + String escape = escapes[c]; + if (escape == null) continue; + if (lastPos < pos) append(string, lastPos, pos-lastPos); + lastPos = pos + 1; + append(escape); + } + if (lastPos < pos) append(string, lastPos, pos-lastPos); + } + + private void escapeAndAppendString(char[] buf, int start, int len) throws IOException { + final char NE = (char)ESCAPE_TABLE.length; + final String[] escapes = ESCAPE_TABLE; + int end = start+len; + int lastPos = start; + int pos; + for (pos=start; pos<end; pos++) { + char c = buf[pos]; + if (c >= NE) continue; + String escape = escapes[c]; + if (escape == null) continue; + if (lastPos < pos) append(buf, lastPos, pos-lastPos); + lastPos = pos + 1; + append(escape); + } + if (lastPos < pos) append(buf, lastPos, pos-lastPos); + } + + public XmlSerializer attribute(String namespace, String name, String value) throws IOException, + IllegalArgumentException, IllegalStateException { + append(' '); + if (namespace != null) { + append(namespace); + append(':'); + } + append(name); + append("=\""); + + escapeAndAppendString(value); + append('"'); + return this; + } + + public void cdsect(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void comment(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void docdecl(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException { + flush(); + } + + public XmlSerializer endTag(String namespace, String name) throws IOException, + IllegalArgumentException, IllegalStateException { + if (mInTag) { + append(" />\n"); + } else { + append("</"); + if (namespace != null) { + append(namespace); + append(':'); + } + append(name); + append(">\n"); + } + mInTag = false; + return this; + } + + public void entityRef(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + private void flushBytes() throws IOException { + int position; + if ((position = mBytes.position()) > 0) { + mBytes.flip(); + mOutputStream.write(mBytes.array(), 0, position); + mBytes.clear(); + } + } + + public void flush() throws IOException { + //Log.i("PackageManager", "flush mPos=" + mPos); + if (mPos > 0) { + if (mOutputStream != null) { + CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); + CoderResult result = mCharset.encode(charBuffer, mBytes, true); + while (true) { + if (result.isError()) { + throw new IOException(result.toString()); + } else if (result.isOverflow()) { + flushBytes(); + result = mCharset.encode(charBuffer, mBytes, true); + continue; + } + break; + } + flushBytes(); + mOutputStream.flush(); + } else { + mWriter.write(mText, 0, mPos); + mWriter.flush(); + } + mPos = 0; + } + } + + public int getDepth() { + throw new UnsupportedOperationException(); + } + + public boolean getFeature(String name) { + throw new UnsupportedOperationException(); + } + + public String getName() { + throw new UnsupportedOperationException(); + } + + public String getNamespace() { + throw new UnsupportedOperationException(); + } + + public String getPrefix(String namespace, boolean generatePrefix) + throws IllegalArgumentException { + throw new UnsupportedOperationException(); + } + + public Object getProperty(String name) { + throw new UnsupportedOperationException(); + } + + public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void processingInstruction(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void setFeature(String name, boolean state) throws IllegalArgumentException, + IllegalStateException { + if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) { + return; + } + throw new UnsupportedOperationException(); + } + + public void setOutput(OutputStream os, String encoding) throws IOException, + IllegalArgumentException, IllegalStateException { + if (os == null) + throw new IllegalArgumentException(); + if (true) { + try { + mCharset = Charset.forName(encoding).newEncoder(); + } catch (IllegalCharsetNameException e) { + throw (UnsupportedEncodingException) (new UnsupportedEncodingException( + encoding).initCause(e)); + } catch (UnsupportedCharsetException e) { + throw (UnsupportedEncodingException) (new UnsupportedEncodingException( + encoding).initCause(e)); + } + mOutputStream = os; + } else { + setOutput( + encoding == null + ? new OutputStreamWriter(os) + : new OutputStreamWriter(os, encoding)); + } + } + + public void setOutput(Writer writer) throws IOException, IllegalArgumentException, + IllegalStateException { + mWriter = writer; + } + + public void setPrefix(String prefix, String namespace) throws IOException, + IllegalArgumentException, IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void setProperty(String name, Object value) throws IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void startDocument(String encoding, Boolean standalone) throws IOException, + IllegalArgumentException, IllegalStateException { + append("<?xml version='1.0' encoding='utf-8' standalone='" + + (standalone ? "yes" : "no") + "' ?>\n"); + } + + public XmlSerializer startTag(String namespace, String name) throws IOException, + IllegalArgumentException, IllegalStateException { + if (mInTag) { + append(">\n"); + } + append('<'); + if (namespace != null) { + append(namespace); + append(':'); + } + append(name); + mInTag = true; + return this; + } + + public XmlSerializer text(char[] buf, int start, int len) throws IOException, + IllegalArgumentException, IllegalStateException { + if (mInTag) { + append(">"); + mInTag = false; + } + escapeAndAppendString(buf, start, len); + return this; + } + + public XmlSerializer text(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + if (mInTag) { + append(">"); + mInTag = false; + } + escapeAndAppendString(text); + return this; + } + +} diff --git a/common/java/com/android/common/NetworkConnectivityListener.java b/common/java/com/android/common/NetworkConnectivityListener.java new file mode 100644 index 0000000..b49b80d --- /dev/null +++ b/common/java/com/android/common/NetworkConnectivityListener.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.common; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import java.util.HashMap; +import java.util.Iterator; + +/** + * A wrapper for a broadcast receiver that provides network connectivity + * state information, independent of network type (mobile, Wi-Fi, etc.). + * @deprecated Code tempted to use this class should simply listen for connectivity intents + * (or poll ConnectivityManager) directly. + * {@hide} + */ +public class NetworkConnectivityListener { + private static final String TAG = "NetworkConnectivityListener"; + private static final boolean DBG = false; + + private Context mContext; + private HashMap<Handler, Integer> mHandlers = new HashMap<Handler, Integer>(); + private State mState; + private boolean mListening; + private String mReason; + private boolean mIsFailover; + + /** Network connectivity information */ + private NetworkInfo mNetworkInfo; + + /** + * In case of a Disconnect, the connectivity manager may have + * already established, or may be attempting to establish, connectivity + * with another network. If so, {@code mOtherNetworkInfo} will be non-null. + */ + private NetworkInfo mOtherNetworkInfo; + + private ConnectivityBroadcastReceiver mReceiver; + + private class ConnectivityBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || + mListening == false) { + Log.w(TAG, "onReceived() called with " + mState.toString() + " and " + intent); + return; + } + + boolean noConnectivity = + intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); + + if (noConnectivity) { + mState = State.NOT_CONNECTED; + } else { + mState = State.CONNECTED; + } + + mNetworkInfo = (NetworkInfo) + intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + mOtherNetworkInfo = (NetworkInfo) + intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO); + + mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON); + mIsFailover = + intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false); + + if (DBG) { + Log.d(TAG, "onReceive(): mNetworkInfo=" + mNetworkInfo + " mOtherNetworkInfo = " + + (mOtherNetworkInfo == null ? "[none]" : mOtherNetworkInfo + + " noConn=" + noConnectivity) + " mState=" + mState.toString()); + } + + // Notifiy any handlers. + Iterator<Handler> it = mHandlers.keySet().iterator(); + while (it.hasNext()) { + Handler target = it.next(); + Message message = Message.obtain(target, mHandlers.get(target)); + target.sendMessage(message); + } + } + }; + + public enum State { + UNKNOWN, + + /** This state is returned if there is connectivity to any network **/ + CONNECTED, + /** + * This state is returned if there is no connectivity to any network. This is set + * to true under two circumstances: + * <ul> + * <li>When connectivity is lost to one network, and there is no other available + * network to attempt to switch to.</li> + * <li>When connectivity is lost to one network, and the attempt to switch to + * another network fails.</li> + */ + NOT_CONNECTED + } + + /** + * Create a new NetworkConnectivityListener. + */ + public NetworkConnectivityListener() { + mState = State.UNKNOWN; + mReceiver = new ConnectivityBroadcastReceiver(); + } + + /** + * This method starts listening for network connectivity state changes. + * @param context + */ + public synchronized void startListening(Context context) { + if (!mListening) { + mContext = context; + + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + context.registerReceiver(mReceiver, filter); + mListening = true; + } + } + + /** + * This method stops this class from listening for network changes. + */ + public synchronized void stopListening() { + if (mListening) { + mContext.unregisterReceiver(mReceiver); + mContext = null; + mNetworkInfo = null; + mOtherNetworkInfo = null; + mIsFailover = false; + mReason = null; + mListening = false; + } + } + + /** + * This methods registers a Handler to be called back onto with the specified what code when + * the network connectivity state changes. + * + * @param target The target handler. + * @param what The what code to be used when posting a message to the handler. + */ + public void registerHandler(Handler target, int what) { + mHandlers.put(target, what); + } + + /** + * This methods unregisters the specified Handler. + * @param target + */ + public void unregisterHandler(Handler target) { + mHandlers.remove(target); + } + + public State getState() { + return mState; + } + + /** + * Return the NetworkInfo associated with the most recent connectivity event. + * @return {@code NetworkInfo} for the network that had the most recent connectivity event. + */ + public NetworkInfo getNetworkInfo() { + return mNetworkInfo; + } + + /** + * If the most recent connectivity event was a DISCONNECT, return + * any information supplied in the broadcast about an alternate + * network that might be available. If this returns a non-null + * value, then another broadcast should follow shortly indicating + * whether connection to the other network succeeded. + * + * @return NetworkInfo + */ + public NetworkInfo getOtherNetworkInfo() { + return mOtherNetworkInfo; + } + + /** + * Returns true if the most recent event was for an attempt to switch over to + * a new network following loss of connectivity on another network. + * @return {@code true} if this was a failover attempt, {@code false} otherwise. + */ + public boolean isFailover() { + return mIsFailover; + } + + /** + * An optional reason for the connectivity state change may have been supplied. + * This returns it. + * @return the reason for the state change, if available, or {@code null} + * otherwise. + */ + public String getReason() { + return mReason; + } +} diff --git a/common/java/com/android/common/XmlUtils.java b/common/java/com/android/common/XmlUtils.java new file mode 100644 index 0000000..dd57e49 --- /dev/null +++ b/common/java/com/android/common/XmlUtils.java @@ -0,0 +1,796 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.common; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import android.util.Xml; + +/** {@hide} */ +public class XmlUtils +{ + + public static void skipCurrentTag(XmlPullParser parser) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + } + } + + public static final int + convertValueToList(CharSequence value, String[] options, int defaultValue) + { + if (null != value) { + for (int i = 0; i < options.length; i++) { + if (value.equals(options[i])) + return i; + } + } + + return defaultValue; + } + + public static final boolean + convertValueToBoolean(CharSequence value, boolean defaultValue) + { + boolean result = false; + + if (null == value) + return defaultValue; + + if (value.equals("1") + || value.equals("true") + || value.equals("TRUE")) + result = true; + + return result; + } + + public static final int + convertValueToInt(CharSequence charSeq, int defaultValue) + { + if (null == charSeq) + return defaultValue; + + String nm = charSeq.toString(); + + // XXX This code is copied from Integer.decode() so we don't + // have to instantiate an Integer! + + int value; + int sign = 1; + int index = 0; + int len = nm.length(); + int base = 10; + + if ('-' == nm.charAt(0)) { + sign = -1; + index++; + } + + if ('0' == nm.charAt(index)) { + // Quick check for a zero by itself + if (index == (len - 1)) + return 0; + + char c = nm.charAt(index + 1); + + if ('x' == c || 'X' == c) { + index += 2; + base = 16; + } else { + index++; + base = 8; + } + } + else if ('#' == nm.charAt(index)) + { + index++; + base = 16; + } + + return Integer.parseInt(nm.substring(index), base) * sign; + } + + public static final int + convertValueToUnsignedInt(String value, int defaultValue) + { + if (null == value) + return defaultValue; + + return parseUnsignedIntAttribute(value); + } + + public static final int + parseUnsignedIntAttribute(CharSequence charSeq) + { + String value = charSeq.toString(); + + long bits; + int index = 0; + int len = value.length(); + int base = 10; + + if ('0' == value.charAt(index)) { + // Quick check for zero by itself + if (index == (len - 1)) + return 0; + + char c = value.charAt(index + 1); + + if ('x' == c || 'X' == c) { // check for hex + index += 2; + base = 16; + } else { // check for octal + index++; + base = 8; + } + } else if ('#' == value.charAt(index)) { + index++; + base = 16; + } + + return (int) Long.parseLong(value.substring(index), base); + } + + /** + * Flatten a Map into an output stream as XML. The map can later be + * read back with readMapXml(). + * + * @param val The map to be flattened. + * @param out Where to write the XML data. + * + * @see #writeMapXml(Map, String, XmlSerializer) + * @see #writeListXml + * @see #writeValueXml + * @see #readMapXml + */ + public static final void writeMapXml(Map val, OutputStream out) + throws XmlPullParserException, java.io.IOException { + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(out, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + writeMapXml(val, null, serializer); + serializer.endDocument(); + } + + /** + * Flatten a List into an output stream as XML. The list can later be + * read back with readListXml(). + * + * @param val The list to be flattened. + * @param out Where to write the XML data. + * + * @see #writeListXml(List, String, XmlSerializer) + * @see #writeMapXml + * @see #writeValueXml + * @see #readListXml + */ + public static final void writeListXml(List val, OutputStream out) + throws XmlPullParserException, java.io.IOException + { + XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(out, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + writeListXml(val, null, serializer); + serializer.endDocument(); + } + + /** + * Flatten a Map into an XmlSerializer. The map can later be read back + * with readThisMapXml(). + * + * @param val The map to be flattened. + * @param name Name attribute to include with this list's tag, or null for + * none. + * @param out XmlSerializer to write the map into. + * + * @see #writeMapXml(Map, OutputStream) + * @see #writeListXml + * @see #writeValueXml + * @see #readMapXml + */ + public static final void writeMapXml(Map val, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException + { + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + Set s = val.entrySet(); + Iterator i = s.iterator(); + + out.startTag(null, "map"); + if (name != null) { + out.attribute(null, "name", name); + } + + while (i.hasNext()) { + Map.Entry e = (Map.Entry)i.next(); + writeValueXml(e.getValue(), (String)e.getKey(), out); + } + + out.endTag(null, "map"); + } + + /** + * Flatten a List into an XmlSerializer. The list can later be read back + * with readThisListXml(). + * + * @param val The list to be flattened. + * @param name Name attribute to include with this list's tag, or null for + * none. + * @param out XmlSerializer to write the list into. + * + * @see #writeListXml(List, OutputStream) + * @see #writeMapXml + * @see #writeValueXml + * @see #readListXml + */ + public static final void writeListXml(List val, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException + { + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "list"); + if (name != null) { + out.attribute(null, "name", name); + } + + int N = val.size(); + int i=0; + while (i < N) { + writeValueXml(val.get(i), null, out); + i++; + } + + out.endTag(null, "list"); + } + + /** + * Flatten a byte[] into an XmlSerializer. The list can later be read back + * with readThisByteArrayXml(). + * + * @param val The byte array to be flattened. + * @param name Name attribute to include with this array's tag, or null for + * none. + * @param out XmlSerializer to write the array into. + * + * @see #writeMapXml + * @see #writeValueXml + */ + public static final void writeByteArrayXml(byte[] val, String name, + XmlSerializer out) + throws XmlPullParserException, java.io.IOException { + + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "byte-array"); + if (name != null) { + out.attribute(null, "name", name); + } + + final int N = val.length; + out.attribute(null, "num", Integer.toString(N)); + + StringBuilder sb = new StringBuilder(val.length*2); + for (int i=0; i<N; i++) { + int b = val[i]; + int h = b>>4; + sb.append(h >= 10 ? ('a'+h-10) : ('0'+h)); + h = b&0xff; + sb.append(h >= 10 ? ('a'+h-10) : ('0'+h)); + } + + out.text(sb.toString()); + + out.endTag(null, "byte-array"); + } + + /** + * Flatten an int[] into an XmlSerializer. The list can later be read back + * with readThisIntArrayXml(). + * + * @param val The int array to be flattened. + * @param name Name attribute to include with this array's tag, or null for + * none. + * @param out XmlSerializer to write the array into. + * + * @see #writeMapXml + * @see #writeValueXml + * @see #readThisIntArrayXml + */ + public static final void writeIntArrayXml(int[] val, String name, + XmlSerializer out) + throws XmlPullParserException, java.io.IOException { + + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "int-array"); + if (name != null) { + out.attribute(null, "name", name); + } + + final int N = val.length; + out.attribute(null, "num", Integer.toString(N)); + + for (int i=0; i<N; i++) { + out.startTag(null, "item"); + out.attribute(null, "value", Integer.toString(val[i])); + out.endTag(null, "item"); + } + + out.endTag(null, "int-array"); + } + + /** + * Flatten an object's value into an XmlSerializer. The value can later + * be read back with readThisValueXml(). + * + * Currently supported value types are: null, String, Integer, Long, + * Float, Double Boolean, Map, List. + * + * @param v The object to be flattened. + * @param name Name attribute to include with this value's tag, or null + * for none. + * @param out XmlSerializer to write the object into. + * + * @see #writeMapXml + * @see #writeListXml + * @see #readValueXml + */ + public static final void writeValueXml(Object v, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException + { + String typeStr; + if (v == null) { + out.startTag(null, "null"); + if (name != null) { + out.attribute(null, "name", name); + } + out.endTag(null, "null"); + return; + } else if (v instanceof String) { + out.startTag(null, "string"); + if (name != null) { + out.attribute(null, "name", name); + } + out.text(v.toString()); + out.endTag(null, "string"); + return; + } else if (v instanceof Integer) { + typeStr = "int"; + } else if (v instanceof Long) { + typeStr = "long"; + } else if (v instanceof Float) { + typeStr = "float"; + } else if (v instanceof Double) { + typeStr = "double"; + } else if (v instanceof Boolean) { + typeStr = "boolean"; + } else if (v instanceof byte[]) { + writeByteArrayXml((byte[])v, name, out); + return; + } else if (v instanceof int[]) { + writeIntArrayXml((int[])v, name, out); + return; + } else if (v instanceof Map) { + writeMapXml((Map)v, name, out); + return; + } else if (v instanceof List) { + writeListXml((List)v, name, out); + return; + } else if (v instanceof CharSequence) { + // XXX This is to allow us to at least write something if + // we encounter styled text... but it means we will drop all + // of the styling information. :( + out.startTag(null, "string"); + if (name != null) { + out.attribute(null, "name", name); + } + out.text(v.toString()); + out.endTag(null, "string"); + return; + } else { + throw new RuntimeException("writeValueXml: unable to write value " + v); + } + + out.startTag(null, typeStr); + if (name != null) { + out.attribute(null, "name", name); + } + out.attribute(null, "value", v.toString()); + out.endTag(null, typeStr); + } + + /** + * Read a HashMap from an InputStream containing XML. The stream can + * previously have been written by writeMapXml(). + * + * @param in The InputStream from which to read. + * + * @return HashMap The resulting map. + * + * @see #readListXml + * @see #readValueXml + * @see #readThisMapXml + * #see #writeMapXml + */ + public static final HashMap readMapXml(InputStream in) + throws XmlPullParserException, java.io.IOException + { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + return (HashMap)readValueXml(parser, new String[1]); + } + + /** + * Read an ArrayList from an InputStream containing XML. The stream can + * previously have been written by writeListXml(). + * + * @param in The InputStream from which to read. + * + * @return HashMap The resulting list. + * + * @see #readMapXml + * @see #readValueXml + * @see #readThisListXml + * @see #writeListXml + */ + public static final ArrayList readListXml(InputStream in) + throws XmlPullParserException, java.io.IOException + { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + return (ArrayList)readValueXml(parser, new String[1]); + } + + /** + * Read a HashMap object from an XmlPullParser. The XML data could + * previously have been generated by writeMapXml(). The XmlPullParser + * must be positioned <em>after</em> the tag that begins the map. + * + * @param parser The XmlPullParser from which to read the map data. + * @param endTag Name of the tag that will end the map, usually "map". + * @param name An array of one string, used to return the name attribute + * of the map's tag. + * + * @return HashMap The newly generated map. + * + * @see #readMapXml + */ + public static final HashMap readThisMapXml(XmlPullParser parser, String endTag, String[] name) + throws XmlPullParserException, java.io.IOException + { + HashMap map = new HashMap(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name); + if (name[0] != null) { + //System.out.println("Adding to map: " + name + " -> " + val); + map.put(name[0], val); + } else { + throw new XmlPullParserException( + "Map value without name attribute: " + parser.getName()); + } + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return map; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read an ArrayList object from an XmlPullParser. The XML data could + * previously have been generated by writeListXml(). The XmlPullParser + * must be positioned <em>after</em> the tag that begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "list". + * @param name An array of one string, used to return the name attribute + * of the list's tag. + * + * @return HashMap The newly generated list. + * + * @see #readListXml + */ + public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name) + throws XmlPullParserException, java.io.IOException + { + ArrayList list = new ArrayList(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name); + list.add(val); + //System.out.println("Adding to list: " + val); + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return list; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read an int[] object from an XmlPullParser. The XML data could + * previously have been generated by writeIntArrayXml(). The XmlPullParser + * must be positioned <em>after</em> the tag that begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "list". + * @param name An array of one string, used to return the name attribute + * of the list's tag. + * + * @return Returns a newly generated int[]. + * + * @see #readListXml + */ + public static final int[] readThisIntArrayXml(XmlPullParser parser, + String endTag, String[] name) + throws XmlPullParserException, java.io.IOException { + + int num; + try { + num = Integer.parseInt(parser.getAttributeValue(null, "num")); + } catch (NullPointerException e) { + throw new XmlPullParserException( + "Need num attribute in byte-array"); + } catch (NumberFormatException e) { + throw new XmlPullParserException( + "Not a number in num attribute in byte-array"); + } + + int[] array = new int[num]; + int i = 0; + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + if (parser.getName().equals("item")) { + try { + array[i] = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } catch (NullPointerException e) { + throw new XmlPullParserException( + "Need value attribute in item"); + } catch (NumberFormatException e) { + throw new XmlPullParserException( + "Not a number in value attribute in item"); + } + } else { + throw new XmlPullParserException( + "Expected item tag at: " + parser.getName()); + } + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return array; + } else if (parser.getName().equals("item")) { + i++; + } else { + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + + parser.getName()); + } + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read a flattened object from an XmlPullParser. The XML data could + * previously have been written with writeMapXml(), writeListXml(), or + * writeValueXml(). The XmlPullParser must be positioned <em>at</em> the + * tag that defines the value. + * + * @param parser The XmlPullParser from which to read the object. + * @param name An array of one string, used to return the name attribute + * of the value's tag. + * + * @return Object The newly generated value object. + * + * @see #readMapXml + * @see #readListXml + * @see #writeValueXml + */ + public static final Object readValueXml(XmlPullParser parser, String[] name) + throws XmlPullParserException, java.io.IOException + { + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + return readThisValueXml(parser, name); + } else if (eventType == parser.END_TAG) { + throw new XmlPullParserException( + "Unexpected end tag at: " + parser.getName()); + } else if (eventType == parser.TEXT) { + throw new XmlPullParserException( + "Unexpected text: " + parser.getText()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Unexpected end of document"); + } + + private static final Object readThisValueXml(XmlPullParser parser, String[] name) + throws XmlPullParserException, java.io.IOException + { + final String valueName = parser.getAttributeValue(null, "name"); + final String tagName = parser.getName(); + + //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName); + + Object res; + + if (tagName.equals("null")) { + res = null; + } else if (tagName.equals("string")) { + String value = ""; + int eventType; + while ((eventType = parser.next()) != parser.END_DOCUMENT) { + if (eventType == parser.END_TAG) { + if (parser.getName().equals("string")) { + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + value); + return value; + } + throw new XmlPullParserException( + "Unexpected end tag in <string>: " + parser.getName()); + } else if (eventType == parser.TEXT) { + value += parser.getText(); + } else if (eventType == parser.START_TAG) { + throw new XmlPullParserException( + "Unexpected start tag in <string>: " + parser.getName()); + } + } + throw new XmlPullParserException( + "Unexpected end of document in <string>"); + } else if (tagName.equals("int")) { + res = Integer.parseInt(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("long")) { + res = Long.valueOf(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("float")) { + res = new Float(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("double")) { + res = new Double(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("boolean")) { + res = Boolean.valueOf(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("int-array")) { + parser.next(); + res = readThisIntArrayXml(parser, "int-array", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else if (tagName.equals("map")) { + parser.next(); + res = readThisMapXml(parser, "map", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else if (tagName.equals("list")) { + parser.next(); + res = readThisListXml(parser, "list", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else { + throw new XmlPullParserException( + "Unknown tag: " + tagName); + } + + // Skip through to end tag. + int eventType; + while ((eventType = parser.next()) != parser.END_DOCUMENT) { + if (eventType == parser.END_TAG) { + if (parser.getName().equals(tagName)) { + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } + throw new XmlPullParserException( + "Unexpected end tag in <" + tagName + ">: " + parser.getName()); + } else if (eventType == parser.TEXT) { + throw new XmlPullParserException( + "Unexpected text in <" + tagName + ">: " + parser.getName()); + } else if (eventType == parser.START_TAG) { + throw new XmlPullParserException( + "Unexpected start tag in <" + tagName + ">: " + parser.getName()); + } + } + throw new XmlPullParserException( + "Unexpected end of document in <" + tagName + ">"); + } + + public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException + { + int type; + while ((type=parser.next()) != parser.START_TAG + && type != parser.END_DOCUMENT) { + ; + } + + if (type != parser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + if (!parser.getName().equals(firstElementName)) { + throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + + ", expected " + firstElementName); + } + } + + public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException + { + int type; + while ((type=parser.next()) != parser.START_TAG + && type != parser.END_DOCUMENT) { + ; + } + } +} |