| /* |
| * Copyright (C) 2015 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.tools.idea.rendering; |
| |
| import com.android.ide.common.rendering.api.AttrResourceValue; |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Splitter; |
| import com.google.common.io.Files; |
| import com.google.common.io.LineProcessor; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Utility methods to extract information from R.txt files. |
| */ |
| class RDotTxtParser { |
| |
| private static final String INT_ID = "int id "; |
| private static final int INT_ID_LEN = INT_ID.length(); |
| private static Logger ourLog; |
| private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); |
| |
| @NotNull |
| static Collection<String> getIdNames(final File rFile) { |
| try { |
| return Files.readLines(rFile, Charsets.UTF_8, new LineProcessor<Collection<String>>() { |
| Collection<String> idNames = new ArrayList<String>(32); |
| |
| @Override |
| public boolean processLine(@NotNull String line) throws IOException { |
| if (!line.startsWith(INT_ID)) { |
| return true; |
| } |
| int i = line.indexOf(' ', INT_ID_LEN); |
| assert i != -1 : "File not in expected format: " + rFile.getPath() + "\n" + |
| "Expected the ids to be in the format int id <name> <number>"; |
| idNames.add(line.substring(INT_ID_LEN, i)); |
| return true; |
| } |
| |
| @Override |
| public Collection<String> getResult() { |
| return idNames; |
| } |
| }); |
| } |
| catch (IOException e) { |
| getLog().warn("Unable to read file: " + rFile.getPath(), e); |
| return Collections.emptyList(); |
| } |
| } |
| |
| /** |
| * For styleable array entries. |
| * <p> |
| * Search R.txt file, {@code r}, for the styleable with {@code styleableName} and return the |
| * array of attribute ids, in the order specified by the list {@code attrs}. Returns null if the |
| * styleable is not found. |
| */ |
| @Nullable |
| static Integer[] getDeclareStyleableArray(File r, final List<AttrResourceValue> attrs, final String styleableName) { |
| try { |
| return Files.readLines(r, Charsets.UTF_8, new LineProcessor<Integer[]>() { |
| private static final String INT_STYLEABLE = "int styleable "; |
| |
| private final String myArrayStart = "int[] styleable " + styleableName + " { "; |
| private final String myEntryStart = INT_STYLEABLE + styleableName + "_"; |
| |
| private Integer[] myValuesList; |
| private String[] myDeclaredAttrs; |
| private int myAttrsFound; |
| |
| @Override |
| public boolean processLine(@NotNull String line) throws IOException { |
| if (line.startsWith(myArrayStart)) { |
| // line must be of the form "int[] styleable name { value1, value2, ..., valueN }" |
| // extract " value1, value2, ..., valueN " |
| String valuesList = line.substring(myArrayStart.length(), line.length() - 1); |
| myValuesList = new Integer[StringUtil.countChars(valuesList, ',') + 1]; |
| myDeclaredAttrs = new String[myValuesList.length]; |
| int idx = 0; |
| |
| for (String s : COMMA_SPLITTER.split(valuesList)) { |
| myValuesList[idx++] = Integer.decode(s); |
| } |
| return true; |
| } |
| |
| if (line.startsWith(myEntryStart)) { |
| assert myValuesList != null : "Entries for a declare-styleable should be after the array declaration."; |
| // line must be of the form "int styleable name value" |
| int lastSpace = line.lastIndexOf(' '); |
| String name = line.substring(INT_STYLEABLE.length(), lastSpace); |
| int index = Integer.parseInt(line.substring(lastSpace + 1)); |
| myDeclaredAttrs[index] = name; |
| myAttrsFound++; |
| // return false if all entries have been found. |
| return myAttrsFound != myDeclaredAttrs.length; |
| } |
| |
| // Not a line we care about, continue processing. |
| return true; |
| } |
| |
| @Override |
| public Integer[] getResult() { |
| if (myValuesList == null) { |
| return null; |
| } |
| // The order of entries in a declare-styleable in the source xml and in R.txt may be different. |
| // It's essential that the order of entries match the order of attrs. So, we reorder the entries. |
| int index = 0; |
| for (AttrResourceValue attr : attrs) { |
| String name = AarResourceClassGenerator.getResourceName(styleableName, attr); |
| for (int i = index; i < myDeclaredAttrs.length; i++) { |
| String declaredAttr = myDeclaredAttrs[i]; |
| if (declaredAttr.equals(name)) { |
| ArrayUtil.swap(myDeclaredAttrs, i, index); |
| ArrayUtil.swap(myValuesList, i, index); |
| break; |
| } |
| } |
| assert myDeclaredAttrs[index].equals(name) : name + " does not equal " + myDeclaredAttrs[index]; |
| index++; |
| } |
| return myValuesList; |
| } |
| }); |
| } |
| catch (IOException e) { |
| return null; |
| } |
| } |
| |
| private static Logger getLog() { |
| if (ourLog == null) { |
| ourLog = Logger.getInstance(RDotTxtParser.class); |
| } |
| return ourLog; |
| } |
| } |