• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.layoutlib.utils;
18 
19 import org.xml.sax.Attributes;
20 import org.xml.sax.SAXException;
21 import org.xml.sax.helpers.DefaultHandler;
22 
23 /**
24  * SAX handler to parser value resource files.
25  */
26 public final class ValueResourceParser extends DefaultHandler {
27 
28     // TODO: reuse definitions from somewhere else.
29     private final static String NODE_RESOURCES = "resources";
30     private final static String NODE_ITEM = "item";
31     private final static String ATTR_NAME = "name";
32     private final static String ATTR_TYPE = "type";
33     private final static String ATTR_PARENT = "parent";
34 
35     // Resource type definition
36     private final static String RES_STYLE = "style";
37     private final static String RES_ATTR = "attr";
38 
39     private final static String DEFAULT_NS_PREFIX = "android:";
40     private final static int DEFAULT_NS_PREFIX_LEN = DEFAULT_NS_PREFIX.length();
41 
42     public interface IValueResourceRepository {
addResourceValue(String resType, ResourceValue value)43         void addResourceValue(String resType, ResourceValue value);
44     }
45 
46     private boolean inResources = false;
47     private int mDepth = 0;
48     private StyleResourceValue mCurrentStyle = null;
49     private ResourceValue mCurrentValue = null;
50     private IValueResourceRepository mRepository;
51     private final boolean mIsFramework;
52 
ValueResourceParser(IValueResourceRepository repository, boolean isFramework)53     public ValueResourceParser(IValueResourceRepository repository, boolean isFramework) {
54         mRepository = repository;
55         mIsFramework = isFramework;
56     }
57 
58     @Override
endElement(String uri, String localName, String qName)59     public void endElement(String uri, String localName, String qName) throws SAXException {
60         if (mCurrentValue != null) {
61             mCurrentValue.setValue(trimXmlWhitespaces(mCurrentValue.getValue()));
62         }
63 
64         if (inResources && qName.equals(NODE_RESOURCES)) {
65             inResources = false;
66         } else if (mDepth == 2) {
67             mCurrentValue = null;
68             mCurrentStyle = null;
69         } else if (mDepth == 3) {
70             mCurrentValue = null;
71         }
72 
73         mDepth--;
74         super.endElement(uri, localName, qName);
75     }
76 
77     @Override
startElement(String uri, String localName, String qName, Attributes attributes)78     public void startElement(String uri, String localName, String qName, Attributes attributes)
79             throws SAXException {
80         try {
81             mDepth++;
82             if (inResources == false && mDepth == 1) {
83                 if (qName.equals(NODE_RESOURCES)) {
84                     inResources = true;
85                 }
86             } else if (mDepth == 2 && inResources == true) {
87                 String type;
88 
89                 // if the node is <item>, we get the type from the attribute "type"
90                 if (NODE_ITEM.equals(qName)) {
91                     type = attributes.getValue(ATTR_TYPE);
92                 } else {
93                     // the type is the name of the node.
94                     type = qName;
95                 }
96 
97                 if (type != null) {
98                     if (RES_ATTR.equals(type) == false) {
99                         // get the resource name
100                         String name = attributes.getValue(ATTR_NAME);
101                         if (name != null) {
102                             if (RES_STYLE.equals(type)) {
103                                 String parent = attributes.getValue(ATTR_PARENT);
104                                 mCurrentStyle = new StyleResourceValue(type, name, parent, mIsFramework);
105                                 mRepository.addResourceValue(type, mCurrentStyle);
106                             } else {
107                                 mCurrentValue = new ResourceValue(type, name, mIsFramework);
108                                 mRepository.addResourceValue(type, mCurrentValue);
109                             }
110                         }
111                     }
112                 }
113             } else if (mDepth == 3 && mCurrentStyle != null) {
114                 // get the resource name
115                 String name = attributes.getValue(ATTR_NAME);
116                 if (name != null) {
117                     // the name can, in some cases, contain a prefix! we remove it.
118                     if (name.startsWith(DEFAULT_NS_PREFIX)) {
119                         name = name.substring(DEFAULT_NS_PREFIX_LEN);
120                     }
121 
122                     mCurrentValue = new ResourceValue(null, name, mIsFramework);
123                     mCurrentStyle.addItem(mCurrentValue);
124                 }
125             }
126         } finally {
127             super.startElement(uri, localName, qName, attributes);
128         }
129     }
130 
131     @Override
characters(char[] ch, int start, int length)132     public void characters(char[] ch, int start, int length) throws SAXException {
133         if (mCurrentValue != null) {
134             String value = mCurrentValue.getValue();
135             if (value == null) {
136                 mCurrentValue.setValue(new String(ch, start, length));
137             } else {
138                 mCurrentValue.setValue(value + new String(ch, start, length));
139             }
140         }
141     }
142 
trimXmlWhitespaces(String value)143     public static String trimXmlWhitespaces(String value) {
144         if (value == null) {
145             return null;
146         }
147 
148         // look for carriage return and replace all whitespace around it by just 1 space.
149         int index;
150 
151         while ((index = value.indexOf('\n')) != -1) {
152             // look for whitespace on each side
153             int left = index - 1;
154             while (left >= 0) {
155                 if (Character.isWhitespace(value.charAt(left))) {
156                     left--;
157                 } else {
158                     break;
159                 }
160             }
161 
162             int right = index + 1;
163             int count = value.length();
164             while (right < count) {
165                 if (Character.isWhitespace(value.charAt(right))) {
166                     right++;
167                 } else {
168                     break;
169                 }
170             }
171 
172             // remove all between left and right (non inclusive) and replace by a single space.
173             String leftString = null;
174             if (left >= 0) {
175                 leftString = value.substring(0, left + 1);
176             }
177             String rightString = null;
178             if (right < count) {
179                 rightString = value.substring(right);
180             }
181 
182             if (leftString != null) {
183                 value = leftString;
184                 if (rightString != null) {
185                     value += " " + rightString;
186                 }
187             } else {
188                 value = rightString != null ? rightString : "";
189             }
190         }
191 
192         // now we un-escape the string
193         int length = value.length();
194         char[] buffer = value.toCharArray();
195 
196         for (int i = 0 ; i < length ; i++) {
197             if (buffer[i] == '\\' && i + 1 < length) {
198                 if (buffer[i+1] == 'u') {
199                     if (i + 5 < length) {
200                         // this is unicode char \u1234
201                         int unicodeChar = Integer.parseInt(new String(buffer, i+2, 4), 16);
202 
203                         // put the unicode char at the location of the \
204                         buffer[i] = (char)unicodeChar;
205 
206                         // offset the rest of the buffer since we go from 6 to 1 char
207                         if (i + 6 < buffer.length) {
208                             System.arraycopy(buffer, i+6, buffer, i+1, length - i - 6);
209                         }
210                         length -= 5;
211                     }
212                 } else {
213                     if (buffer[i+1] == 'n') {
214                         // replace the 'n' char with \n
215                         buffer[i+1] = '\n';
216                     }
217 
218                     // offset the buffer to erase the \
219                     System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
220                     length--;
221                 }
222             }
223         }
224 
225         return new String(buffer, 0, length);
226     }
227 }
228