blob: 95359801e02376922db1d52c06bbb670eb206672 [file] [log] [blame]
/*
* Copyright (C) 2014 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.avdmanager;
import com.android.annotations.VisibleForTesting;
import com.android.tools.idea.templates.TemplateUtils;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Allows access to Device Skin Layout files. The layout file syntax is of the form:
* <pre>
* key {
* subkey {
* arbitrary-subkey {
* keypair-key keypair-value
* }
* }
* }
* </pre>
*/
public class SkinLayoutDefinition {
@VisibleForTesting static final Pattern ourQuerySeparator = Pattern.compile("\\.");
@VisibleForTesting static final Pattern ourWhitespacePattern = Pattern.compile("\\s+");
@Nullable
public static SkinLayoutDefinition parseFile(@NotNull File file) {
String contents = TemplateUtils.readTextFile(file);
if (contents == null) {
return null;
}
return loadFromTokens(Splitter.on(ourWhitespacePattern).omitEmptyStrings().trimResults().split(contents).iterator());
}
/**
* Populate myProperties and myChildren from the token stream
* @param tokens a queue of string tokens
*/
@VisibleForTesting
static SkinLayoutDefinition loadFromTokens(Iterator<String> tokens) {
String key;
String value;
SkinLayoutDefinition definition = new SkinLayoutDefinition();
while (tokens.hasNext()) {
key = tokens.next();
if (key.equals("}")) { // We're done with this block, return
break;
} else {
value = tokens.next();
if (value.equals("{")) { // Start of a nested block, recursively load that block
definition.myChildren.put(key, loadFromTokens(tokens));
} else { // Otherwise, it's a string property, and we'll store it
definition.myProperties.put(key, value);
}
}
}
return definition;
}
private Map<String, String> myProperties = Maps.newHashMap();
private Map<String, SkinLayoutDefinition> myChildren = Maps.newHashMap();
private SkinLayoutDefinition() {
// Private constructor. Use #parseFile to obtain an instance
}
/**
* Returns the property associated with the given query string or null if no such property exists.
* Example: Given
* <pre>
* foo {
* bar {
* abc 123
* }
* baz {
* hello world
* }
* }
* </pre>
* The query string "foo.bar.abc" would return the string "123" and the query string "foo.baz.hello" would return "world."
* The query string "foo.bar.def" would return null because the key referenced does not exist.
* The query string "foo.bar" would return null because it represents an incomplete path.
* @param queryString a dot-separated list of string keys
*/
@Nullable
public String get(@NotNull String queryString) {
return get(Splitter.on(ourQuerySeparator).split(queryString).iterator());
}
@Nullable
private String get(@NotNull Iterator<String> queryIterator) {
if (!queryIterator.hasNext()) {
return null;
}
String key = queryIterator.next();
if (!queryIterator.hasNext()) {
return myProperties.get(key);
} else {
SkinLayoutDefinition child = myChildren.get(key);
if (child != null) {
return child.get(queryIterator);
} else {
return null;
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
makeString(sb, 1);
return sb.toString();
}
/**
* @param depth number of 2-space indents to apply
*/
private void makeString(@NotNull StringBuilder sb, int depth) {
sb.append("{\n");
for (String key : myProperties.keySet()) {
appendSpace(sb, depth);
sb.append(key);
sb.append(" ");
sb.append(myProperties.get(key));
sb.append("\n");
}
for (String key : myChildren.keySet()) {
appendSpace(sb, depth);
sb.append(key);
sb.append(" ");
myChildren.get(key).makeString(sb, depth + 1);
}
appendSpace(sb, depth - 1);
sb.append("}\n");
}
private static void appendSpace(@NotNull StringBuilder sb, int depth) {
for (int i = 0; i < depth; i++) {
sb.append(" ");
}
}
}