| /* |
| * Copyright (C) 2008 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.ide.common.resources; |
| |
| import com.android.ide.common.rendering.api.AttrResourceValue; |
| import com.android.ide.common.rendering.api.DeclareStyleableResourceValue; |
| import com.android.ide.common.rendering.api.ResourceValue; |
| import com.android.ide.common.rendering.api.StyleResourceValue; |
| import com.android.ide.common.res2.ValueXmlHelper; |
| import com.android.resources.ResourceType; |
| |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * SAX handler to parse value resource files. |
| */ |
| public final class ValueResourceParser extends DefaultHandler { |
| |
| // TODO: reuse definitions from somewhere else. |
| private static final String NODE_RESOURCES = "resources"; |
| private static final String NODE_ITEM = "item"; |
| private static final String ATTR_NAME = "name"; |
| private static final String ATTR_TYPE = "type"; |
| private static final String ATTR_PARENT = "parent"; |
| private static final String ATTR_VALUE = "value"; |
| |
| private static final String DEFAULT_NS_PREFIX = "android:"; |
| private static final int DEFAULT_NS_PREFIX_LEN = DEFAULT_NS_PREFIX.length(); |
| |
| public interface IValueResourceRepository { |
| void addResourceValue(ResourceValue value); |
| boolean hasResourceValue(ResourceType type, String name); |
| } |
| |
| private boolean inResources = false; |
| private int mDepth = 0; |
| private ResourceValue mCurrentValue = null; |
| private StyleResourceValue mCurrentStyle = null; |
| private DeclareStyleableResourceValue mCurrentDeclareStyleable = null; |
| private AttrResourceValue mCurrentAttr; |
| private IValueResourceRepository mRepository; |
| private final boolean mIsFramework; |
| |
| public ValueResourceParser(IValueResourceRepository repository, boolean isFramework) { |
| mRepository = repository; |
| mIsFramework = isFramework; |
| } |
| |
| @Override |
| public void endElement(String uri, String localName, String qName) throws SAXException { |
| if (mCurrentValue != null) { |
| mCurrentValue.setValue( |
| ValueXmlHelper.unescapeResourceString(mCurrentValue.getValue(), false, true)); |
| } |
| |
| if (inResources && qName.equals(NODE_RESOURCES)) { |
| inResources = false; |
| } else if (mDepth == 2) { |
| mCurrentValue = null; |
| mCurrentStyle = null; |
| mCurrentDeclareStyleable = null; |
| mCurrentAttr = null; |
| } else if (mDepth == 3) { |
| mCurrentValue = null; |
| if (mCurrentDeclareStyleable != null) { |
| mCurrentAttr = null; |
| } |
| } |
| |
| mDepth--; |
| super.endElement(uri, localName, qName); |
| } |
| |
| @Override |
| public void startElement(String uri, String localName, String qName, Attributes attributes) |
| throws SAXException { |
| try { |
| mDepth++; |
| if (inResources == false && mDepth == 1) { |
| if (qName.equals(NODE_RESOURCES)) { |
| inResources = true; |
| } |
| } else if (mDepth == 2 && inResources == true) { |
| ResourceType type = getType(qName, attributes); |
| |
| if (type != null) { |
| // get the resource name |
| String name = attributes.getValue(ATTR_NAME); |
| if (name != null) { |
| switch (type) { |
| case STYLE: |
| String parent = attributes.getValue(ATTR_PARENT); |
| mCurrentStyle = new StyleResourceValue(type, name, parent, |
| mIsFramework); |
| mRepository.addResourceValue(mCurrentStyle); |
| break; |
| case DECLARE_STYLEABLE: |
| mCurrentDeclareStyleable = new DeclareStyleableResourceValue( |
| type, name, mIsFramework); |
| mRepository.addResourceValue(mCurrentDeclareStyleable); |
| break; |
| case ATTR: |
| mCurrentAttr = new AttrResourceValue(type, name, mIsFramework); |
| mRepository.addResourceValue(mCurrentAttr); |
| break; |
| default: |
| mCurrentValue = new ResourceValue(type, name, mIsFramework); |
| mRepository.addResourceValue(mCurrentValue); |
| break; |
| } |
| } |
| } |
| } else if (mDepth == 3) { |
| // get the resource name |
| String name = attributes.getValue(ATTR_NAME); |
| if (name != null) { |
| |
| if (mCurrentStyle != null) { |
| // is the attribute in the android namespace? |
| boolean isFrameworkAttr = mIsFramework; |
| if (name.startsWith(DEFAULT_NS_PREFIX)) { |
| name = name.substring(DEFAULT_NS_PREFIX_LEN); |
| isFrameworkAttr = true; |
| } |
| |
| mCurrentValue = new ResourceValue(null, name, mIsFramework); |
| mCurrentStyle.addValue(mCurrentValue, isFrameworkAttr); |
| } else if (mCurrentDeclareStyleable != null) { |
| // is the attribute in the android namespace? |
| boolean isFramework = mIsFramework; |
| if (name.startsWith(DEFAULT_NS_PREFIX)) { |
| name = name.substring(DEFAULT_NS_PREFIX_LEN); |
| isFramework = true; |
| } |
| |
| mCurrentAttr = new AttrResourceValue(ResourceType.ATTR, name, isFramework); |
| mCurrentDeclareStyleable.addValue(mCurrentAttr); |
| |
| // also add it to the repository. |
| mRepository.addResourceValue(mCurrentAttr); |
| |
| } else if (mCurrentAttr != null) { |
| // get the enum/flag value |
| String value = attributes.getValue(ATTR_VALUE); |
| |
| try { |
| // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we |
| // use Long.decode instead. |
| mCurrentAttr.addValue(name, (int)(long)Long.decode(value)); |
| } catch (NumberFormatException e) { |
| // pass, we'll just ignore this value |
| } |
| |
| } |
| } |
| } else if (mDepth == 4 && mCurrentAttr != null) { |
| // get the enum/flag name |
| String name = attributes.getValue(ATTR_NAME); |
| String value = attributes.getValue(ATTR_VALUE); |
| |
| try { |
| // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we |
| // use Long.decode instead. |
| mCurrentAttr.addValue(name, (int)(long)Long.decode(value)); |
| } catch (NumberFormatException e) { |
| // pass, we'll just ignore this value |
| } |
| } |
| } finally { |
| super.startElement(uri, localName, qName, attributes); |
| } |
| } |
| |
| private ResourceType getType(String qName, Attributes attributes) { |
| String typeValue; |
| |
| // if the node is <item>, we get the type from the attribute "type" |
| if (NODE_ITEM.equals(qName)) { |
| typeValue = attributes.getValue(ATTR_TYPE); |
| } else { |
| // the type is the name of the node. |
| typeValue = qName; |
| } |
| |
| ResourceType type = ResourceType.getEnum(typeValue); |
| return type; |
| } |
| |
| |
| @Override |
| public void characters(char[] ch, int start, int length) throws SAXException { |
| if (mCurrentValue != null) { |
| String value = mCurrentValue.getValue(); |
| if (value == null) { |
| mCurrentValue.setValue(new String(ch, start, length)); |
| } else { |
| mCurrentValue.setValue(value + new String(ch, start, length)); |
| } |
| } |
| } |
| |
| |
| } |