• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package ohos;
17 
18 import java.nio.ByteBuffer;
19 import java.nio.ByteOrder;
20 import java.nio.charset.StandardCharsets;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Optional;
24 
25 /**
26  * Resources Parser.
27  *
28  */
29 public class ResourcesParser {
30     /**
31      * Parses resources default id.
32      */
33     public static final int RESOURCE_DEFAULT_ID = -1;
34 
35     private static final int VERSION_BYTE_LENGTH = 128;
36     private static final int TAG_BYTE_LENGTH = 4;
37     private static final String LEFT_BRACKET = "<";
38     private static final String RIGHT_BRACKET = ">";
39     private static final String VERTICAL = "vertical";
40     private static final String HORIZONTAL = "horizontal";
41     private static final String DARK = "dark";
42     private static final String LIGHT = "light";
43     private static final String MCC = "mcc";
44     private static final String MNC = "mnc";
45     private static final String CONFIG_CONJUNCTION = "-";
46     private static final String MCC_CONJUNCTION = "_";
47     private static final String BASE = "base";
48     private static final int CHAR_LENGTH = 1;
49     private static final String EMPTY_STRING = "";
50     private enum ResType {
51         Values(0, "Values"),
52         Animator(1, "Animator"),
53         Drawable(2, "Drawable"),
54         Layout(3, "Layout"),
55         Menu(4, "Menu"),
56         Mipmap(5, "Mipmap"),
57         Raw(6, "Raw"),
58         Xml(7, "Xml"),
59         Integer(8, "Integer"),
60         String(9, "String"),
61         StrArray(10, "StrArray"),
62         IntArray(11, "IntArray"),
63         Boolean(12, "Boolean"),
64         Dimen(13, "Dimen"),
65         Color(14, "Color"),
66         Id(15, "Id"),
67         Theme(16, "Theme"),
68         Plurals(17, "Plurals"),
69         Float(18, "Float"),
70         Media(19, "Media"),
71         Prof(20, "Prof"),
72         Svg(21, "Svg"),
73         Pattern(22, "Pattern");
74 
75         private final int index;
76         private final String type;
77 
ResType(int index, String type)78         private ResType(int index, String type) {
79             this.index = index;
80             this.type = type;
81         }
82 
getType(int index)83         public static String getType(int index) {
84             for (ResType resType : ResType.values()) {
85                 if (resType.getIndex() == index) {
86                     return resType.type;
87                 }
88             }
89             return "";
90         }
91 
getIndex()92         public int getIndex() {
93             return index;
94         }
getType()95         public String getType() {
96             return type;
97         }
98     }
99 
100     private enum DeviceType {
101         Phone(0, "phone"),
102         Tablet(1, "tablet"),
103         Car(2, "car"),
104         Pc(3, "pc"),
105         Tv(4, "tv"),
106         Speaker(5, "speaker"),
107         Wearable(6, "wearable"),
108         Glasses(7, "glasses"),
109         Headset(8, "headset");
110 
111         private final int index;
112         private final String type;
DeviceType(int index, String type)113         private DeviceType(int index, String type) {
114             this.index = index;
115             this.type = type;
116         }
117 
getType(int index)118         public static String getType(int index) {
119             for (DeviceType deviceType : DeviceType.values()) {
120                 if (deviceType.getIndex() == index) {
121                     return deviceType.type;
122                 }
123             }
124             return "";
125         }
126 
getIndex()127         public int getIndex() {
128             return index;
129         }
getType()130         public String getType() {
131             return type;
132         }
133     }
134 
135     private enum Resolution {
136         Nodpi(-2, "nodpi"),
137         Anydpi(-1, "anydpi"),
138         Sdpi(120, "sdpi"),
139         Mdpi(160, "mdpi"),
140         Tvdpi(213, "tvdpi"),
141         Ldpi(240, "ldpi"),
142         Xldpi(320, "xldpi"),
143         Xxldpi(480, "xxldpi"),
144         Xxxldpi(640, "xxxldpi");
145 
146         private final int index;
147         private final String type;
Resolution(int index, String type)148         private Resolution(int index, String type) {
149             this.index = index;
150             this.type = type;
151         }
getType(int index)152         public static String getType(int index) {
153             for (Resolution resolution : Resolution.values()) {
154                 if (resolution.getIndex() == index) {
155                     return resolution.type;
156                 }
157             }
158             return "";
159         }
160 
getIndex()161         public int getIndex() {
162             return index;
163         }
getType()164         public String getType() {
165             return type;
166         }
167     }
168 
169     private enum ConfigType {
170         Language, Region, Resolution, Direction, DeviceType, Script, LightMode, MCC, MNC
171     }
172 
173     private static final Log LOG = new Log(ResourcesParser.class.toString());
174 
175     /**
176      * Key Param.
177      */
178     static class KeyParam {
179         int keyType;
180         int value;
181     }
182 
183     /**
184      * Config Index.
185      */
186     static class ConfigIndex {
187         String tag;
188         int offset;
189         int keyCount;
190         KeyParam[] params;
191     }
192 
193     /**
194      * Data Item.
195      */
196     static class DataItem {
197         int size;
198         int type;
199         int id;
200         String value;
201         String name;
202     }
203 
204     /**
205      * Get resource value by resource id.
206      *
207      * @param resourceId resource id
208      * @param data resource index data
209      * @return the resourceId value
210      * @throws BundleException IOException.
211      */
getResourceById(int resourceId, byte[] data)212     static String getResourceById(int resourceId, byte[] data) throws BundleException {
213         String resourceIdValue = "";
214         if (data == null || data.length <= 0 || resourceId == RESOURCE_DEFAULT_ID) {
215             LOG.error("ResourcesParser::getIconPath data byte or ResourceId is null");
216             return resourceIdValue;
217         }
218 
219         List<String> result = getResource(resourceId, data);
220         if (result != null && result.size() > 0 && result.get(0) != null && !EMPTY_STRING.equals(result.get(0))) {
221             resourceIdValue = result.get(0).substring(0, result.get(0).length() - 1);
222         }
223         return resourceIdValue;
224     }
225 
226     /**
227      * Get base resource value by resource id.
228      *
229      * @param resourceId resource id
230      * @param data resource index data
231      * @return the resource value
232      * @throws BundleException IOException.
233      */
getBaseResourceById(int resourceId, byte[] data)234     static String getBaseResourceById(int resourceId, byte[] data) throws BundleException {
235         String resourceIdValue = "";
236         if (data == null || data.length <= 0 || resourceId == RESOURCE_DEFAULT_ID) {
237             LOG.error("ResourcesParser::getBaseResourceById data byte or ResourceId is null");
238             return resourceIdValue;
239         }
240         resourceIdValue = getBaseResource(resourceId, data);
241         if (resourceIdValue != null && !EMPTY_STRING.equals(resourceIdValue)) {
242             resourceIdValue = resourceIdValue.substring(0, resourceIdValue.length() - 1);
243         }
244         return resourceIdValue;
245     }
246 
247     /**
248      * Get base resource.
249      *
250      * @param resId resource id
251      * @param data resource index data array
252      * @return the resource value
253      * @throws BundleException IOException.
254      */
getBaseResource(int resId, byte[] data)255     static String getBaseResource(int resId, byte[] data) {
256         ByteBuffer byteBuf = ByteBuffer.wrap(data);
257         byteBuf.order(ByteOrder.LITTLE_ENDIAN);
258         byte[] version = new byte[VERSION_BYTE_LENGTH];
259         byteBuf.get(version);
260         byteBuf.getInt();
261         int configCount = byteBuf.getInt();
262         Optional<ConfigIndex> optionalConfigIndex = loadBaseConfig(byteBuf, configCount);
263         if (!optionalConfigIndex.isPresent()) {
264             LOG.error("ResourcesParser::getBaseResource configIndex is null");
265             return "";
266         }
267         return readBaseItem(resId, optionalConfigIndex.get(), byteBuf);
268     }
269 
270     /**
271      * Load index config.
272      *
273      * @param bufBuf config byte buffer
274      * @param count config count
275      * @return the base config index
276      * @throws BundleException IOException.
277      */
loadBaseConfig(ByteBuffer bufBuf, int count)278     static  Optional<ConfigIndex> loadBaseConfig(ByteBuffer bufBuf, int count) {
279         for (int i = 0; i < count; i++) {
280             ConfigIndex cfg = new ConfigIndex();
281             byte[] tag = new byte[TAG_BYTE_LENGTH];
282             bufBuf.get(tag);
283             cfg.tag = new String(tag, StandardCharsets.UTF_8);
284             cfg.offset = bufBuf.getInt();
285             cfg.keyCount = bufBuf.getInt();
286             cfg.params = new KeyParam[cfg.keyCount];
287             for (int j = 0; j < cfg.keyCount; j++) {
288                 cfg.params[j] = new KeyParam();
289                 cfg.params[j].keyType = bufBuf.getInt();
290                 cfg.params[j].value = bufBuf.getInt();
291             }
292             if (cfg.keyCount == 0) {
293                 return Optional.of(cfg);
294             }
295         }
296         return Optional.empty();
297     }
298 
299     /**
300      * Read base config item.
301      *
302      * @param resId resource id
303      * @param configIndex the config index
304      * @param buf config byte buffer
305      * @return the base item
306      * @throws BundleException IOException.
307      */
readBaseItem(int resId, ConfigIndex configIndex, ByteBuffer buf)308     static String readBaseItem(int resId, ConfigIndex configIndex, ByteBuffer buf) {
309         buf.rewind();
310         buf.position(configIndex.offset);
311         byte[] tag = new byte[TAG_BYTE_LENGTH];
312         buf.get(tag);
313         int count = buf.getInt();
314         for (int i = 0; i < count; i++) {
315             int id = buf.getInt();
316             int offset = buf.getInt();
317             if (id == resId) {
318                 buf.rewind();
319                 buf.position(offset);
320                 DataItem item = readItem(buf);
321                 return item.value;
322             }
323         }
324         return "";
325     }
326 
327     /**
328      * Get Icon resource.
329      *
330      * @param resId resource id
331      * @param data resource index data array
332      * @return the result
333      * @throws BundleException IOException.
334      */
getResource(int resId, byte[] data)335     static List<String> getResource(int resId, byte[] data) {
336         ByteBuffer byteBuf = ByteBuffer.wrap(data);
337         byteBuf.order(ByteOrder.LITTLE_ENDIAN);
338         byte[] version = new byte[VERSION_BYTE_LENGTH];
339         byteBuf.get(version);
340         byteBuf.getInt();
341         int configCount = byteBuf.getInt();
342         List<ConfigIndex> cfg = loadConfig(byteBuf, configCount);
343         return readAllItem(resId, cfg, byteBuf);
344     }
345 
346     /**
347      * Load index config.
348      *
349      * @param bufBuf config byte buffer
350      * @param count config count
351      * @return the config list
352      * @throws BundleException IOException.
353      */
loadConfig(ByteBuffer bufBuf, int count)354     static List<ConfigIndex> loadConfig(ByteBuffer bufBuf, int count) {
355         List<ConfigIndex> configList = new ArrayList<>(count);
356         for (int i = 0; i < count; i++) {
357             ConfigIndex cfg = new ConfigIndex();
358             byte[] tag = new byte[TAG_BYTE_LENGTH];
359             bufBuf.get(tag);
360             cfg.tag = new String(tag, StandardCharsets.UTF_8);
361             cfg.offset = bufBuf.getInt();
362             cfg.keyCount = bufBuf.getInt();
363             cfg.params = new KeyParam[cfg.keyCount];
364             for (int j = 0; j < cfg.keyCount; j++) {
365                 cfg.params[j] = new KeyParam();
366                 cfg.params[j].keyType = bufBuf.getInt();
367                 cfg.params[j].value = bufBuf.getInt();
368             }
369 
370             configList.add(cfg);
371         }
372         return configList;
373     }
374 
375     /**
376      * Read all config item.
377      *
378      * @param resId resource id
379      * @param configs the config list
380      * @param buf config byte buffer
381      * @return the item list
382      * @throws BundleException IOException.
383      */
readAllItem(int resId, List<ConfigIndex> configs, ByteBuffer buf)384     static List<String> readAllItem(int resId, List<ConfigIndex> configs, ByteBuffer buf) {
385         List<String> result = new ArrayList<>();
386         for (ConfigIndex index : configs) {
387             buf.rewind();
388             buf.position(index.offset);
389             byte[] tag = new byte[TAG_BYTE_LENGTH];
390             buf.get(tag);
391             int count = buf.getInt();
392             for (int i = 0; i < count; i++) {
393                 int id = buf.getInt();
394                 int offset = buf.getInt();
395                 if (id == resId) {
396                     buf.rewind();
397                     buf.position(offset);
398                     DataItem item = readItem(buf);
399                     result.add(item.value);
400                     break;
401                 }
402             }
403         }
404         return result;
405     }
406 
407     /**
408      * Read the config item.
409      *
410      * @param buf config byte buffer
411      * @return the item info
412      */
readItem(ByteBuffer buf)413     static DataItem readItem(ByteBuffer buf) {
414         DataItem item = new DataItem();
415         item.size = buf.getInt();
416         item.type = buf.getInt();
417         item.id = buf.getInt();
418         int len = buf.getShort();
419         byte[] value = new byte[len];
420         buf.get(value);
421         item.value = new String(value, StandardCharsets.UTF_8);
422         len = buf.getShort();
423         byte[] name = new byte[len];
424         buf.get(name);
425         item.name = new String(name, StandardCharsets.UTF_8);
426         return item;
427     }
428 
429     /**
430      * Read all config item.
431      *
432      * @param data config byte buffer
433      * @return the item info.
434      */
getAllDataItem(byte[] data)435     static List<ResourceIndexResult> getAllDataItem(byte[] data) {
436         ByteBuffer byteBuf = ByteBuffer.wrap(data);
437         byteBuf.order(ByteOrder.LITTLE_ENDIAN);
438         byte[] version = new byte[VERSION_BYTE_LENGTH];
439         byteBuf.get(version);
440         byteBuf.getInt();
441         int configCount = byteBuf.getInt();
442         List<ConfigIndex> cfg = loadConfig(byteBuf, configCount);
443         return readDataAllItem(cfg, byteBuf);
444     }
445 
446     /**
447      * Read all config item.
448      *
449      * @param configs the config list
450      * @param buf config byte buffer
451      * @return the item list
452      */
readDataAllItem(List<ConfigIndex> configs, ByteBuffer buf)453     static List<ResourceIndexResult> readDataAllItem(List<ConfigIndex> configs, ByteBuffer buf) {
454         List<ResourceIndexResult> resourceIndexResults = new ArrayList<>();
455         for (ConfigIndex index : configs) {
456             String configClass = convertConfigIndexToString(index);
457             buf.rewind();
458             buf.position(index.offset);
459             byte[] tag = new byte[TAG_BYTE_LENGTH];
460             buf.get(tag);
461             int count = buf.getInt();
462             int position = buf.position();
463             for (int i = 0; i < count; ++i) {
464                 buf.position(position);
465                 buf.getInt();
466                 int offset = buf.getInt();
467                 position = buf.position();
468                 buf.rewind();
469                 buf.position(offset);
470                 DataItem item = readItem(buf);
471                 resourceIndexResults.add(parseDataItems(item, configClass));
472             }
473         }
474         return resourceIndexResults;
475     }
476 
477     /**
478      * convert DataItems to ResourceIndexResult.
479      *
480      * @param item Indicates the DataItem.
481      *  @return the final ResourceIndexResult
482      */
parseDataItems(DataItem item, String configClass)483     static ResourceIndexResult parseDataItems(DataItem item, String configClass) {
484         ResourceIndexResult resourceIndexResult = new ResourceIndexResult();
485         resourceIndexResult.configClass = configClass;
486         if (item != null) {
487             resourceIndexResult.type = ResType.getType(item.type);
488             resourceIndexResult.id = item.id;
489             resourceIndexResult.name = item.name.substring(0, item.name.length() - 1);
490             if (item.type == ResType.StrArray.getIndex() || item.type == ResType.IntArray.getIndex()
491                     || item.type == ResType.Theme.getIndex() || item.type == ResType.Plurals.getIndex()
492                     || item.type == ResType.Pattern.getIndex()) {
493                 byte[] bytes =
494                         item.value.substring(0, item.value.length() - 1).getBytes(StandardCharsets.UTF_8);
495                 resourceIndexResult.value = convertBytesToString(bytes);
496             } else {
497                 resourceIndexResult.value = item.value.substring(0, item.value.length() - 1);
498             }
499         }
500         return resourceIndexResult;
501     }
502 
503     /**
504      * convert bytes to string.
505      *
506      * @param data Indicates the bytes of data.
507      * @return the final string
508      */
convertBytesToString(byte[] data)509     static String convertBytesToString(byte[] data) {
510         StringBuilder result = new StringBuilder();
511         ByteBuffer byteBuf = ByteBuffer.wrap(data);
512         byteBuf.order(ByteOrder.LITTLE_ENDIAN);
513         while(byteBuf.hasRemaining()) {
514             result.append(LEFT_BRACKET);
515             int len = byteBuf.getShort();
516             byte[] value = new byte[len + CHAR_LENGTH];
517             byteBuf.get(value);
518             String item = new String(value, StandardCharsets.UTF_8);
519             result.append(item, 0, item.length() - 1);
520             result.append(RIGHT_BRACKET);
521         }
522         return result.toString();
523     }
524 
525     /**
526      * convert config to string.
527      *
528      * @param configIndex Indicates the configIndex.
529      *  @return the final string
530      */
convertConfigIndexToString(ConfigIndex configIndex)531     static String convertConfigIndexToString(ConfigIndex configIndex) {
532         StringBuilder configClass = new StringBuilder();
533         int lastKeyType = -1;
534         for (int i = 0; i < configIndex.keyCount; ++i) {
535             KeyParam param = configIndex.params[i];
536             if (param.keyType == ConfigType.Language.ordinal() || param.keyType == ConfigType.Region.ordinal()
537                     || param.keyType == ConfigType.Script.ordinal()) {
538                 if (EMPTY_STRING.equals(configClass.toString())) {
539                     configClass.append(parseAscii(param.value));
540                 } else {
541                     if (lastKeyType == ConfigType.Language.ordinal() ||
542                             lastKeyType == ConfigType.Region.ordinal() || lastKeyType == ConfigType.Script.ordinal()) {
543                         configClass.append(MCC_CONJUNCTION).append(parseAscii(param.value));
544                     } else {
545                         configClass.append(CONFIG_CONJUNCTION).append(parseAscii(param.value));
546                     }
547                 }
548                 lastKeyType = param.keyType;
549             } else if (param.keyType == ConfigType.Resolution.ordinal()) {
550                 if (EMPTY_STRING.equals(configClass.toString())) {
551                     configClass.append(Resolution.getType(param.value));
552                 } else {
553                     configClass.append(CONFIG_CONJUNCTION).append(Resolution.getType(param.value));
554                 }
555             } else if (param.keyType == ConfigType.Direction.ordinal()) {
556                 if (EMPTY_STRING.equals(configClass.toString())) {
557                     configClass.append(param.value == 0 ? VERTICAL : HORIZONTAL);
558                 } else {
559                     configClass.append(CONFIG_CONJUNCTION).append(param.value == 0 ? VERTICAL : HORIZONTAL);
560                 }
561             } else if (param.keyType == ConfigType.DeviceType.ordinal()) {
562                 if (EMPTY_STRING.equals(configClass.toString())) {
563                     configClass.append(DeviceType.getType(param.value));
564                 } else {
565                     configClass.append(CONFIG_CONJUNCTION).append(DeviceType.getType(param.value));
566                 }
567             } else if (param.keyType == ConfigType.LightMode.ordinal()) {
568                 if (EMPTY_STRING.equals(configClass.toString())) {
569                     configClass.append(param.value == 0 ? DARK : LIGHT);
570                 } else {
571                     configClass.append(CONFIG_CONJUNCTION).append(param.value == 0 ? DARK : LIGHT);
572                 }
573             } else if (param.keyType == ConfigType.MCC.ordinal()) {
574                 if (EMPTY_STRING.equals(configClass.toString())) {
575                     configClass.append(MCC).append(String.valueOf(param.value));
576                 } else {
577                     configClass.append(MCC_CONJUNCTION).append(MCC).append(String.valueOf(param.value));
578                 }
579             } else if (param.keyType == ConfigType.MNC.ordinal()) {
580                 if (EMPTY_STRING.equals(configClass.toString())) {
581                     configClass.append(MNC).append(fillUpZero(String.valueOf(param.value), 3));
582                 } else {
583                     configClass.append(MCC_CONJUNCTION).append(MNC).append(fillUpZero(String.valueOf(param.value), 3));
584                 }
585             }
586         }
587         if (EMPTY_STRING.equals(configClass.toString())) {
588             configClass = new StringBuilder(BASE);
589         }
590         return configClass.toString();
591     }
592 
593     /**
594      * convert integer to string.
595      *
596      * @param value Indicates the Integer.
597      *  @return the final string
598      */
parseAscii(Integer value)599     private static String parseAscii(Integer value) {
600         StringBuilder result = new StringBuilder();
601         while(value > 0) {
602             result.insert(0, (char) (value & 0xFF));
603             value = value >> 8;
604         }
605         return result.toString();
606     }
607 
608     /**
609      * fillup zero to string.
610      * @param inputString Indicates the string should to be filled.
611      * @param length Indicates the final length of String.
612      * @return the final string
613      */
fillUpZero(String inputString, int length)614     private static String fillUpZero(String inputString, int length) {
615         if (inputString.length() >= length) {
616             return inputString;
617         }
618         StringBuilder result = new StringBuilder();
619         while(result.length() < length - inputString.length()) {
620             result.append('0');
621         }
622         result.append(inputString);
623         return result.toString();
624     }
625 }
626 
627