1 package org.robolectric.shadows; 2 3 import static org.robolectric.res.android.Errors.NO_ERROR; 4 5 import android.os.Build.VERSION_CODES; 6 import org.robolectric.annotation.Implementation; 7 import org.robolectric.annotation.Implements; 8 import org.robolectric.res.android.Ref; 9 import org.robolectric.res.android.Registries; 10 import org.robolectric.res.android.ResXMLParser; 11 import org.robolectric.res.android.ResXMLTree; 12 import org.robolectric.res.android.ResourceTypes.Res_value; 13 import org.xmlpull.v1.XmlPullParserException; 14 15 @Implements( 16 className = "android.content.res.XmlBlock", 17 isInAndroidSdk = false, 18 shadowPicker = ShadowBaseXmlBlock.Picker.class) 19 public class ShadowXmlBlock extends ShadowBaseXmlBlock { 20 21 @Implementation nativeCreate(byte[] bArray, int off, int len)22 protected static long nativeCreate(byte[] bArray, int off, int len) { 23 if (bArray == null) { 24 throw new NullPointerException(); 25 } 26 27 int bLen = bArray.length; 28 if (off < 0 || off >= bLen || len < 0 || len > bLen || (off + len) > bLen) { 29 throw new IndexOutOfBoundsException(); 30 } 31 32 // todo: optimize 33 byte[] b = new byte[len]; 34 System.arraycopy(bArray, off, b, 0, len); 35 36 ResXMLTree osb = new ResXMLTree(null); 37 osb.setTo(b, len, true); 38 // env->ReleaseByteArrayElements(bArray, b, 0); 39 40 if (osb.getError() != NO_ERROR) { 41 throw new IllegalArgumentException(); 42 } 43 44 return Registries.NATIVE_RES_XML_TREES.register(osb); 45 } 46 47 @Implementation nativeGetStringBlock(long obj)48 protected static long nativeGetStringBlock(long obj) { 49 ResXMLTree osb = Registries.NATIVE_RES_XML_TREES.getNativeObject(obj); 50 // if (osb == NULL) { 51 // jniThrowNullPointerException(env, NULL); 52 // return 0; 53 // } 54 55 return osb.getStrings().getNativePtr(); 56 } 57 58 @Implementation(maxSdk = VERSION_CODES.P) nativeCreateParseState(long obj)59 protected static long nativeCreateParseState(long obj) { 60 ResXMLTree osb = Registries.NATIVE_RES_XML_TREES.getNativeObject(obj); 61 // if (osb == NULL) { 62 // jniThrowNullPointerException(env, NULL); 63 // return 0; 64 // } 65 66 ResXMLParser st = new ResXMLParser(osb); 67 // if (st == NULL) { 68 // jniThrowException(env, "java/lang/OutOfMemoryError", NULL); 69 // return 0; 70 // } 71 72 st.restart(); 73 74 return Registries.NATIVE_RES_XML_PARSERS.register(st); 75 } 76 77 @Implementation(minSdk = VERSION_CODES.Q) nativeCreateParseState(long obj, int resid)78 protected static long nativeCreateParseState(long obj, int resid) { 79 ResXMLTree osb = Registries.NATIVE_RES_XML_TREES.getNativeObject(obj); 80 // if (osb == NULL) { 81 // jniThrowNullPointerException(env, NULL); 82 // return 0; 83 // } 84 85 ResXMLParser st = new ResXMLParser(osb); 86 // if (st == NULL) { 87 // jniThrowException(env, "java/lang/OutOfMemoryError", NULL); 88 // return 0; 89 // } 90 91 st.setSourceResourceId(resid); 92 st.restart(); 93 94 return Registries.NATIVE_RES_XML_PARSERS.register(st); 95 } 96 97 @Implementation nativeNext(long state)98 protected static int nativeNext(long state) throws XmlPullParserException { 99 ResXMLParser st = getResXMLParser(state); 100 if (st == null) { 101 return ResXMLParser.event_code_t.END_DOCUMENT; 102 } 103 104 do { 105 int code = st.next(); 106 switch (code) { 107 case ResXMLParser.event_code_t.START_TAG: 108 return 2; 109 case ResXMLParser.event_code_t.END_TAG: 110 return 3; 111 case ResXMLParser.event_code_t.TEXT: 112 return 4; 113 case ResXMLParser.event_code_t.START_DOCUMENT: 114 return 0; 115 case ResXMLParser.event_code_t.END_DOCUMENT: 116 return 1; 117 case ResXMLParser.event_code_t.BAD_DOCUMENT: 118 // goto bad; 119 throw new XmlPullParserException("Corrupt XML binary file"); 120 default: 121 break; 122 } 123 124 } while (true); 125 } 126 127 @Implementation nativeGetNamespace(long state)128 protected static int nativeGetNamespace(long state) { 129 ResXMLParser resXMLParser = getResXMLParser(state); 130 if (resXMLParser == null) { 131 return -1; 132 } 133 return resXMLParser.getElementNamespaceID(); 134 } 135 136 @Implementation nativeGetName(long state)137 protected static int nativeGetName(long state) { 138 ResXMLParser resXMLParser = getResXMLParser(state); 139 if (resXMLParser == null) { 140 return -1; 141 } 142 return resXMLParser.getElementNameID(); 143 } 144 145 @Implementation nativeGetText(long state)146 protected static int nativeGetText(long state) { 147 ResXMLParser resXMLParser = getResXMLParser(state); 148 if (resXMLParser == null) { 149 return -1; 150 } 151 return resXMLParser.getTextID(); 152 } 153 154 @Implementation nativeGetLineNumber(long state)155 protected static int nativeGetLineNumber(long state) { 156 ResXMLParser resXMLParser = getResXMLParser(state); 157 return resXMLParser.getLineNumber(); 158 } 159 160 @Implementation nativeGetAttributeCount(long state)161 protected static int nativeGetAttributeCount(long state) { 162 ResXMLParser resXMLParser = getResXMLParser(state); 163 return resXMLParser.getAttributeCount(); 164 } 165 166 @Implementation nativeGetAttributeNamespace(long state, int idx)167 protected static int nativeGetAttributeNamespace(long state, int idx) { 168 ResXMLParser resXMLParser = getResXMLParser(state); 169 return resXMLParser.getAttributeNamespaceID(idx); 170 } 171 172 @Implementation nativeGetAttributeName(long state, int idx)173 protected static int nativeGetAttributeName(long state, int idx) { 174 ResXMLParser resXMLParser = getResXMLParser(state); 175 return resXMLParser.getAttributeNameID(idx); 176 } 177 178 @Implementation nativeGetAttributeResource(long state, int idx)179 protected static int nativeGetAttributeResource(long state, int idx) { 180 ResXMLParser resXMLParser = getResXMLParser(state); 181 return resXMLParser.getAttributeNameResID(idx); 182 } 183 184 @Implementation nativeGetAttributeDataType(long state, int idx)185 protected static int nativeGetAttributeDataType(long state, int idx) { 186 ResXMLParser resXMLParser = getResXMLParser(state); 187 return resXMLParser.getAttributeDataType(idx); 188 } 189 190 @Implementation nativeGetAttributeData(long state, int idx)191 protected static int nativeGetAttributeData(long state, int idx) { 192 ResXMLParser resXMLParser = getResXMLParser(state); 193 return resXMLParser.getAttributeData(idx); 194 } 195 196 @Implementation nativeGetAttributeStringValue(long state, int idx)197 protected static int nativeGetAttributeStringValue(long state, int idx) { 198 ResXMLParser resXMLParser = getResXMLParser(state); 199 return resXMLParser.getAttributeValueStringID(idx); 200 } 201 202 @Implementation nativeGetIdAttribute(long state)203 protected static int nativeGetIdAttribute(long state) { 204 ResXMLParser resXMLParser = getResXMLParser(state); 205 int idx = resXMLParser.indexOfID(); 206 return idx >= 0 ? resXMLParser.getAttributeValueStringID(idx) : -1; 207 } 208 209 @Implementation nativeGetClassAttribute(long state)210 protected static int nativeGetClassAttribute(long state) { 211 ResXMLParser resXMLParser = getResXMLParser(state); 212 int idx = resXMLParser.indexOfClass(); 213 return idx >= 0 ? resXMLParser.getAttributeValueStringID(idx) : -1; 214 } 215 216 @Implementation nativeGetStyleAttribute(long state)217 protected static int nativeGetStyleAttribute(long state) { 218 ResXMLParser resXMLParser = getResXMLParser(state); 219 int idx = resXMLParser.indexOfStyle(); 220 if (idx < 0) { 221 return 0; 222 } 223 224 final Ref<Res_value> valueRef = new Ref<>(new Res_value()); 225 if (resXMLParser.getAttributeValue(idx, valueRef) < 0) { 226 return 0; 227 } 228 Res_value value = valueRef.get(); 229 230 return value.dataType == org.robolectric.res.android.ResourceTypes.Res_value.TYPE_REFERENCE 231 || value.dataType == org.robolectric.res.android.ResourceTypes.Res_value.TYPE_ATTRIBUTE 232 ? value.data 233 : 0; 234 } 235 236 @Implementation nativeGetAttributeIndex(long token, String ns, String name)237 protected static int nativeGetAttributeIndex(long token, String ns, String name) { 238 ResXMLParser st = getResXMLParser(token); 239 if (st == null || name == null) { 240 throw new NullPointerException(); 241 } 242 243 int nsLen = 0; 244 if (ns != null) { 245 nsLen = ns.length(); 246 } 247 248 return st.indexOfAttribute(ns, nsLen, name, name.length()); 249 } 250 251 @Implementation(minSdk = VERSION_CODES.Q) nativeGetSourceResId(long state)252 protected static int nativeGetSourceResId(long state) { 253 ResXMLParser st = getResXMLParser(state); 254 if (st == null) { 255 return 0; 256 } else { 257 return st.getSourceResourceId(); 258 } 259 } 260 261 @Implementation nativeDestroyParseState(long state)262 protected static void nativeDestroyParseState(long state) { 263 Registries.NATIVE_RES_XML_PARSERS.unregister(state); 264 } 265 266 @Implementation nativeDestroy(long obj)267 protected static void nativeDestroy(long obj) { 268 Registries.NATIVE_RES_XML_TREES.unregister(obj); 269 } 270 getResXMLParser(long state)271 private static ResXMLParser getResXMLParser(long state) { 272 return Registries.NATIVE_RES_XML_PARSERS.peekNativeObject(state); 273 } 274 275 /** Shadow of XmlBlock.Parser. */ 276 @Implements(className = "android.content.res.XmlBlock$Parser", isInAndroidSdk = false) 277 public static class ShadowParser { 278 private int sourceResourceId; 279 setSourceResourceId(int sourceResourceId)280 void setSourceResourceId(int sourceResourceId) { 281 this.sourceResourceId = sourceResourceId; 282 } 283 getSourceResourceId()284 int getSourceResourceId() { 285 return sourceResourceId; 286 } 287 } 288 } 289