blob: 9746c35d8e65bac8bedaea663c9f229407a44888 [file] [log] [blame]
/*
* 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;
}
}