1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.common.resources.deprecated; 18 19 import com.android.ide.common.rendering.api.ArrayResourceValueImpl; 20 import com.android.ide.common.rendering.api.AttrResourceValueImpl; 21 import com.android.ide.common.rendering.api.ResourceNamespace; 22 import com.android.ide.common.rendering.api.ResourceReference; 23 import com.android.ide.common.rendering.api.ResourceValue; 24 import com.android.ide.common.rendering.api.ResourceValueImpl; 25 import com.android.ide.common.rendering.api.StyleItemResourceValue; 26 import com.android.ide.common.rendering.api.StyleItemResourceValueImpl; 27 import com.android.ide.common.rendering.api.StyleResourceValueImpl; 28 import com.android.ide.common.rendering.api.StyleableResourceValueImpl; 29 import com.android.ide.common.resources.ValueXmlHelper; 30 import com.android.resources.ResourceType; 31 32 import org.xml.sax.Attributes; 33 import org.xml.sax.SAXException; 34 import org.xml.sax.helpers.DefaultHandler; 35 36 import com.google.common.base.Strings; 37 38 import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX; 39 import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX_LEN; 40 import static com.android.SdkConstants.ATTR_NAME; 41 import static com.android.SdkConstants.ATTR_PARENT; 42 import static com.android.SdkConstants.ATTR_VALUE; 43 import static com.android.SdkConstants.TAG_RESOURCES; 44 45 /** 46 * @deprecated This class is part of an obsolete resource repository system that is no longer used 47 * in production code. The class is preserved temporarily for LayoutLib tests. 48 */ 49 @Deprecated 50 public final class ValueResourceParser extends DefaultHandler { 51 52 private static final ResourceReference TMP_REF = 53 new ResourceReference(ResourceNamespace.RES_AUTO, ResourceType.STRING, "_tmp"); 54 55 public interface IValueResourceRepository { addResourceValue(ResourceValue value)56 void addResourceValue(ResourceValue value); 57 } 58 59 private boolean inResources; 60 private int mDepth; 61 private ResourceValueImpl mCurrentValue; 62 private ArrayResourceValueImpl mArrayResourceValue; 63 private StyleResourceValueImpl mCurrentStyle; 64 private StyleableResourceValueImpl mCurrentDeclareStyleable; 65 private AttrResourceValueImpl mCurrentAttr; 66 private final IValueResourceRepository mRepository; 67 private final boolean mIsFramework; 68 private final String mLibraryName; 69 ValueResourceParser(IValueResourceRepository repository, boolean isFramework, String libraryName)70 public ValueResourceParser(IValueResourceRepository repository, boolean isFramework, String libraryName) { 71 mRepository = repository; 72 mIsFramework = isFramework; 73 mLibraryName = libraryName; 74 } 75 76 @Override endElement(String uri, String localName, String qName)77 public void endElement(String uri, String localName, String qName) throws SAXException { 78 if (mCurrentValue != null) { 79 String value = mCurrentValue.getValue(); 80 value = value == null ? "" : ValueXmlHelper.unescapeResourceString(value, false, true); 81 mCurrentValue.setValue(value); 82 } 83 84 if (inResources && qName.equals(TAG_RESOURCES)) { 85 inResources = false; 86 } else if (mDepth == 2) { 87 mCurrentValue = null; 88 mCurrentStyle = null; 89 mCurrentDeclareStyleable = null; 90 mCurrentAttr = null; 91 mArrayResourceValue = null; 92 } else if (mDepth == 3) { 93 if (mArrayResourceValue != null && mCurrentValue != null) { 94 mArrayResourceValue.addElement(mCurrentValue.getValue()); 95 } 96 mCurrentValue = null; 97 //noinspection VariableNotUsedInsideIf 98 if (mCurrentDeclareStyleable != null) { 99 mCurrentAttr = null; 100 } 101 } 102 103 mDepth--; 104 super.endElement(uri, localName, qName); 105 } 106 107 @Override startElement(String uri, String localName, String qName, Attributes attributes)108 public void startElement(String uri, String localName, String qName, Attributes attributes) 109 throws SAXException { 110 try { 111 ResourceNamespace namespace = ResourceNamespace.fromBoolean(mIsFramework); 112 mDepth++; 113 if (!inResources && mDepth == 1) { 114 if (qName.equals(TAG_RESOURCES)) { 115 inResources = true; 116 } 117 } else if (mDepth == 2 && inResources) { 118 ResourceType type = 119 ResourceType.fromXmlTag( 120 new Object(), (t) -> qName, (t, name) -> attributes.getValue(name)); 121 122 if (type != null) { 123 // get the resource name 124 String name = attributes.getValue(ATTR_NAME); 125 if (name != null) { 126 switch (type) { 127 case STYLE: 128 String parent = attributes.getValue(ATTR_PARENT); 129 mCurrentStyle = 130 new StyleResourceValueImpl( 131 namespace, name, parent, mLibraryName); 132 mRepository.addResourceValue(mCurrentStyle); 133 break; 134 case STYLEABLE: 135 mCurrentDeclareStyleable = 136 new StyleableResourceValueImpl( 137 namespace, name, null, mLibraryName); 138 mRepository.addResourceValue(mCurrentDeclareStyleable); 139 break; 140 case ATTR: 141 mCurrentAttr = 142 new AttrResourceValueImpl(namespace, name, mLibraryName); 143 mRepository.addResourceValue(mCurrentAttr); 144 break; 145 case ARRAY: 146 mArrayResourceValue = 147 new ArrayResourceValueImpl(namespace, name, mLibraryName); 148 mRepository.addResourceValue(mArrayResourceValue); 149 break; 150 default: 151 mCurrentValue = 152 new ResourceValueImpl( 153 namespace, type, name, null, mLibraryName); 154 mRepository.addResourceValue(mCurrentValue); 155 break; 156 } 157 } 158 } 159 } else if (mDepth == 3) { 160 // get the resource name 161 String name = attributes.getValue(ATTR_NAME); 162 if (!Strings.isNullOrEmpty(name)) { 163 if (mCurrentStyle != null) { 164 mCurrentValue = 165 new StyleItemResourceValueImpl( 166 mCurrentStyle.getNamespace(), name, null, mLibraryName); 167 mCurrentStyle.addItem((StyleItemResourceValue) mCurrentValue); 168 } else if (mCurrentDeclareStyleable != null) { 169 if (name.startsWith(ANDROID_NS_NAME_PREFIX)) { 170 name = name.substring(ANDROID_NS_NAME_PREFIX_LEN); 171 } 172 173 mCurrentAttr = new AttrResourceValueImpl(namespace, name, mLibraryName); 174 mCurrentDeclareStyleable.addValue(mCurrentAttr); 175 176 // also add it to the repository. 177 mRepository.addResourceValue(mCurrentAttr); 178 179 } else if (mCurrentAttr != null) { 180 // get the enum/flag value 181 String value = attributes.getValue(ATTR_VALUE); 182 183 try { 184 // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we 185 // use Long.decode instead. 186 mCurrentAttr.addValue(name, Long.decode(value).intValue(), null); 187 } catch (NumberFormatException e) { 188 // pass, we'll just ignore this value 189 } 190 } 191 } else //noinspection VariableNotUsedInsideIf 192 if (mArrayResourceValue != null) { 193 // Create a temporary resource value to hold the item's value. The value is 194 // not added to the repository, since it's just a holder. The value will be set 195 // in the `characters` method and then added to mArrayResourceValue in `endElement`. 196 mCurrentValue = new ResourceValueImpl(TMP_REF, null); 197 } 198 } else if (mDepth == 4 && mCurrentAttr != null) { 199 // get the enum/flag name 200 String name = attributes.getValue(ATTR_NAME); 201 String value = attributes.getValue(ATTR_VALUE); 202 203 try { 204 // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we 205 // use Long.decode instead. 206 mCurrentAttr.addValue(name, Long.decode(value).intValue(), null); 207 } catch (NumberFormatException e) { 208 // pass, we'll just ignore this value 209 } 210 } 211 } finally { 212 super.startElement(uri, localName, qName, attributes); 213 } 214 } 215 216 @Override characters(char[] ch, int start, int length)217 public void characters(char[] ch, int start, int length) { 218 if (mCurrentValue != null) { 219 String value = mCurrentValue.getValue(); 220 if (value == null) { 221 mCurrentValue.setValue(new String(ch, start, length)); 222 } else { 223 mCurrentValue.setValue(value + new String(ch, start, length)); 224 } 225 } 226 } 227 } 228