@@ -66,6 +97,14 @@ public final class DelegateManagernull
+ * if no such mapping has been made.
+ */
+ public E get(int key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ public E get(int key, E valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED || mValues[i].get() == null) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i].get();
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(int key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ }
+ }
+ }
+
+ /**
+ * Alias for {@link #delete(int)}.
+ */
+ public void remove(int key) {
+ delete(key);
+ }
+
+ /**
+ * Removes the mapping at the specified index.
+ */
+ public void removeAt(int index) {
+ if (mValues[index] != DELETED) {
+ mValues[index] = DELETED;
+ mGarbage = true;
+ }
+ }
+
+ private void gc() {
+ int n = mSize;
+ int o = 0;
+ int[] keys = mKeys;
+ WeakReference>[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ WeakReference> val = values[i];
+
+ // Don't keep any non DELETED values, but only the one that still have a valid
+ // reference.
+ if (val != DELETED && val.get() != null) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ int newSize = ArrayUtils.idealIntArraySize(mSize);
+ if (newSize < mKeys.length) {
+ int[] nkeys = new int[newSize];
+ WeakReference>[] nvalues = new WeakReference[newSize];
+
+ System.arraycopy(mKeys, 0, nkeys, 0, newSize);
+ System.arraycopy(mValues, 0, nvalues, 0, newSize);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, E value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = new WeakReference(value);
+ } else {
+ i = ~i;
+
+ if (i < mSize && (mValues[i] == DELETED || mValues[i].get() == null)) {
+ mKeys[i] = key;
+ mValues[i] = new WeakReference(value);
+ return;
+ }
+
+ if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~binarySearch(mKeys, 0, mSize, key);
+ }
+
+ if (mSize >= mKeys.length) {
+ int n = ArrayUtils.idealIntArraySize(mSize + 1);
+
+ int[] nkeys = new int[n];
+ WeakReference>[] nvalues = new WeakReference[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ if (mSize - i != 0) {
+ // Log.e("SparseArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = new WeakReference(value);
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, returns
+ * the key from the indexth key-value mapping that this
+ * SparseArray stores.
+ */
+ public int keyAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, returns
+ * the value from the indexth key-value mapping that this
+ * SparseArray stores.
+ */
+ public E valueAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return (E) mValues[index].get();
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, sets a new
+ * value for the indexth key-value mapping that this
+ * SparseArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = new WeakReference(value);
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(int key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i].get() == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseArray.
+ */
+ public void clear() {
+ int n = mSize;
+ WeakReference>[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(int key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) {
+ gc();
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ int n = ArrayUtils.idealIntArraySize(pos + 1);
+
+ int[] nkeys = new int[n];
+ WeakReference>[] nvalues = new WeakReference[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = new WeakReference(value);
+ mSize = pos + 1;
+ }
+
+ private boolean hasReclaimedRefs() {
+ for (int i = 0 ; i < mSize ; i++) {
+ if (mValues[i].get() == null) { // DELETED.get() never returns null.
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static int binarySearch(int[] a, int start, int len, int key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+
+ private int[] mKeys;
+ private WeakReference>[] mValues;
+ private int mSize;
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index ab01a3949d618..e6dc646487cba 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -16,6 +16,8 @@
package libcore.icu;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
import java.util.Locale;
/**
@@ -27,80 +29,101 @@ import java.util.Locale;
*/
public class ICU_Delegate {
+ // --- Java delegates
+
+ @LayoutlibDelegate
/*package*/ static String toLowerCase(String s, String localeName) {
return s.toLowerCase();
}
+ @LayoutlibDelegate
/*package*/ static String toUpperCase(String s, String localeName) {
return s.toUpperCase();
}
// --- Native methods accessing ICU's database.
+ @LayoutlibDelegate
/*package*/ static String[] getAvailableBreakIteratorLocalesNative() {
return new String[0];
}
+ @LayoutlibDelegate
/*package*/ static String[] getAvailableCalendarLocalesNative() {
return new String[0];
}
+ @LayoutlibDelegate
/*package*/ static String[] getAvailableCollatorLocalesNative() {
return new String[0];
}
+ @LayoutlibDelegate
/*package*/ static String[] getAvailableDateFormatLocalesNative() {
return new String[0];
}
+ @LayoutlibDelegate
/*package*/ static String[] getAvailableLocalesNative() {
return new String[0];
}
+ @LayoutlibDelegate
/*package*/ static String[] getAvailableNumberFormatLocalesNative() {
return new String[0];
}
+ @LayoutlibDelegate
/*package*/ static String getCurrencyCodeNative(String locale) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static int getCurrencyFractionDigitsNative(String currencyCode) {
return 0;
}
+ @LayoutlibDelegate
/*package*/ static String getCurrencySymbolNative(String locale, String currencyCode) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static String getDisplayCountryNative(String countryCode, String locale) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static String getDisplayLanguageNative(String languageCode, String locale) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static String getDisplayVariantNative(String variantCode, String locale) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static String getISO3CountryNative(String locale) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static String getISO3LanguageNative(String locale) {
return "";
}
+ @LayoutlibDelegate
/*package*/ static String[] getISOLanguagesNative() {
return Locale.getISOLanguages();
}
+ @LayoutlibDelegate
/*package*/ static String[] getISOCountriesNative() {
return Locale.getISOCountries();
}
+ @LayoutlibDelegate
/*package*/ static boolean initLocaleDataImpl(String locale, LocaleData result) {
// Used by Calendar.
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
deleted file mode 100644
index a068ae273651c..0000000000000
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2009 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.layoutlib.bridge;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-
-import junit.framework.TestCase;
-
-public class TestClassReplacement extends TestCase {
-
- public void testClassReplacements() {
- // TODO: we want to test all the classes. For now only, no classes pass the test.
-// final String[] classes = CreateInfo.RENAMED_CLASSES;
- final String[] classes = new String[] {
-// "android.graphics.Paint", "android.graphics._Original_Paint",
-// "android.graphics.Canvas", "android.graphics._Original_Canvas",
- };
- final int count = classes.length;
- for (int i = 0 ; i < count ; i += 2) {
- loadAndCompareClasses(classes[i], classes[i+1]);
- }
- }
-
- private void loadAndCompareClasses(String newClassName, String oldClassName) {
- // load the classes
- try {
- Class> newClass = TestClassReplacement.class.getClassLoader().loadClass(newClassName);
- Class> oldClass = TestClassReplacement.class.getClassLoader().loadClass(oldClassName);
-
- compare(newClass, oldClass);
- } catch (ClassNotFoundException e) {
- fail("Failed to load class: " + e.getMessage());
- }
- }
-
- private void compare(Class> newClass, Class> oldClass) {
- // first compare the methods.
- Method[] newClassMethods = newClass.getDeclaredMethods();
- Method[] oldClassMethods = oldClass.getDeclaredMethods();
-
- for (Method oldMethod : oldClassMethods) {
- // we ignore anything that starts with native. This is because the class we are looking
- // at has already been modified to remove the native modifiers.
- if (oldMethod.getName().startsWith("native")) {
- continue;
- }
-
- // or static and private
- int privateStatic = Modifier.STATIC | Modifier.PRIVATE;
- if ((oldMethod.getModifiers() & privateStatic) == privateStatic) {
- continue;
- }
-
- boolean found = false;
- for (Method newMethod : newClassMethods) {
-
- if (compareMethods(newClass, newMethod, oldClass, oldMethod)) {
- found = true;
- break;
- }
- }
-
- if (found == false) {
- // compute a full class name that's long but not too long.
- StringBuilder sb = new StringBuilder(oldMethod.getName() + "(");
- Type[] params = oldMethod.getGenericParameterTypes();
- for (int j = 0; j < params.length; j++) {
- if (params[j] instanceof Class) {
- Class theClass = (Class)params[j];
- sb.append(theClass.getName());
- int dimensions = 0;
- while (theClass.isArray()) {
- dimensions++;
- theClass = theClass.getComponentType();
- }
- for (int i = 0; i < dimensions; i++) {
- sb.append("[]");
- }
-
- } else {
- sb.append(params[j].toString());
- }
- if (j < (params.length - 1))
- sb.append(",");
- }
- sb.append(")");
-
- fail(String.format("Missing %1$s.%2$s", newClass.getName(), sb.toString()));
- }
- }
-
- // TODO: check (somehow?) that the methods that were removed from the original class
- // have been put back in the new class!
- // For this we need the original unmodified class (ie renamed, but w/o the methods removed)
- }
-
- private boolean compareMethods(Class> newClass, Method newMethod,
- Class> oldClass, Method oldMethod) {
- // first check the name of the method
- if (newMethod.getName().equals(oldMethod.getName()) == false) {
- return false;
- }
-
- // check the return value
- Class> oldReturnType = oldMethod.getReturnType();
- // if it's the old class, or if it's a inner class of the oldclass, we need to change this.
- oldReturnType = adapt(oldReturnType, newClass, oldClass);
-
- // compare the return types
- Class> newReturnType = newMethod.getReturnType();
- if (newReturnType.equals(oldReturnType) == false) {
- return false;
- }
-
- // now check the parameters type.
- Class>[] oldParameters = oldMethod.getParameterTypes();
- Class>[] newParemeters = newMethod.getParameterTypes();
- if (oldParameters.length != newParemeters.length) {
- return false;
- }
-
- for (int i = 0 ; i < oldParameters.length ; i++) {
- if (newParemeters[i].equals(adapt(oldParameters[i], newClass, oldClass)) == false) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Adapts a class to deal with renamed classes.
- * For instance if old class is android.graphics._Original_Paint and the
- * new class is android.graphics.Paint and the class to adapt is
- * android.graphics._Original_Paint$Cap, then the method will return a
- * {@link Class} object representing android.graphics.Paint$Cap.
- *
- * This method will also ensure that all renamed classes contains all the proper inner classes
- * that they should be declaring.
- * @param theClass the class to adapt
- * @param newClass the new class object
- * @param oldClass the old class object
- * @return the adapted class.
- * @throws ClassNotFoundException
- */
- private Class> adapt(Class> theClass, Class> newClass, Class> oldClass) {
- // only look for a new class if it's not primitive as Class.forName() would fail otherwise.
- if (theClass.isPrimitive() == false) {
- String n = theClass.getName().replace(oldClass.getName(), newClass.getName());
- try {
- return Class.forName(n);
- } catch (ClassNotFoundException e) {
- fail("Missing class: " + n);
- }
- }
-
- return theClass;
- }
-}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
index a4140e3cc906a..d3218dbd5bd3d 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
@@ -21,6 +21,8 @@ import com.android.tools.layoutlib.create.CreateInfo;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
import junit.framework.TestCase;
@@ -78,10 +80,15 @@ public class TestDelegates extends TestCase {
}
private void compare(Class> originalClass, Class> delegateClass) throws SecurityException {
- Method[] originalMethods = originalClass.getDeclaredMethods();
+ List