getResourceTypes();
+
+ /**
+ * Returns whether the file generated a resource of a specific type.
+ * @param type The {@link ResourceType}
+ */
+ public abstract boolean hasResources(ResourceType type);
+
+ /**
+ * Returns the value of a resource generated by this file by {@link ResourceType} and name.
+ * If no resource match, null
is returned.
+ * @param type the type of the resource.
+ * @param name the name of the resource.
+ */
+ public abstract ResourceValue getValue(ResourceType type, String name);
+
+ @Override
+ public String toString() {
+ return mFile.toString();
+ }
+}
+
diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFolder.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFolder.java
new file mode 100644
index 0000000000..f73f210bbd
--- /dev/null
+++ b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFolder.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2018 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 app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated;
+
+import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile;
+import app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder;
+import com.android.SdkConstants;
+import com.android.ide.common.resources.configuration.Configurable;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.utils.SdkUtils;
+import com.google.common.annotations.VisibleForTesting;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ * in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public final class ResourceFolder implements Configurable {
+ final ResourceFolderType mType;
+ final FolderConfiguration mConfiguration;
+ IAbstractFolder mFolder;
+ List mFiles;
+ Map mNames;
+ private final ResourceRepository mRepository;
+
+ /**
+ * Creates a new {@link ResourceFolder}
+ * @param type The type of the folder
+ * @param config The configuration of the folder
+ * @param folder The associated {@link IAbstractFolder} object.
+ * @param repository The associated {@link ResourceRepository}
+ */
+ protected ResourceFolder(ResourceFolderType type, FolderConfiguration config,
+ app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder folder, ResourceRepository repository) {
+ mType = type;
+ mConfiguration = config;
+ mFolder = folder;
+ mRepository = repository;
+ }
+
+ /**
+ * Processes a file and adds it to its parent folder resource.
+ *
+ * @param file the underlying resource file.
+ * @param kind the file change kind.
+ * @param context a context object with state for the current update, such
+ * as a place to stash errors encountered
+ * @return the {@link ResourceFile} that was created.
+ */
+ public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind,
+ ScanningContext context) {
+ // look for this file if it's already been created
+ ResourceFile resFile = getFile(file, context);
+
+ if (resFile == null) {
+ if (kind != ResourceDeltaKind.REMOVED) {
+ // create a ResourceFile for it.
+
+ resFile = createResourceFile(file);
+ resFile.load(context);
+
+ // add it to the folder
+ addFile(resFile);
+ }
+ } else {
+ if (kind == ResourceDeltaKind.REMOVED) {
+ removeFile(resFile, context);
+ } else {
+ resFile.update(context);
+ }
+ }
+
+ return resFile;
+ }
+
+ private ResourceFile createResourceFile(IAbstractFile file) {
+ // check if that's a single or multi resource type folder. We have a special case
+ // for ID generating resource types (layout/menu, and XML drawables, etc.).
+ // MultiResourceFile handles the case when several resource types come from a single file
+ // (values files).
+
+ ResourceFile resFile;
+ if (mType != ResourceFolderType.VALUES) {
+ if (FolderTypeRelationship.isIdGeneratingFolderType(mType) &&
+ SdkUtils.endsWithIgnoreCase(file.getName(), SdkConstants.DOT_XML)) {
+ List types = FolderTypeRelationship.getRelatedResourceTypes(mType);
+ ResourceType primaryType = types.get(0);
+ resFile = new IdGeneratingResourceFile(file, this, primaryType);
+ } else {
+ resFile = new SingleResourceFile(file, this);
+ }
+ } else {
+ resFile = new MultiResourceFile(file, this);
+ }
+ return resFile;
+ }
+
+ /**
+ * Adds a {@link ResourceFile} to the folder.
+ *
+ * @param file The {@link ResourceFile}.
+ */
+ @VisibleForTesting
+ public void addFile(ResourceFile file) {
+ if (mFiles == null) {
+ int initialSize = 16;
+ if (mRepository.isFrameworkRepository()) {
+ String name = mFolder.getName();
+ // Pick some reasonable initial sizes for framework data structures
+ // since they are typically (a) large and (b) their sizes are roughly known
+ // in advance
+ switch (mType) {
+ case DRAWABLE: {
+ // See if it's one of the -mdpi, -hdpi etc folders which
+ // are large (~1250 items)
+ int index = name.indexOf('-');
+ if (index == -1) {
+ initialSize = 230; // "drawable" folder
+ } else {
+ index = name.indexOf('-', index + 1);
+ if (index == -1) {
+ // One of the "drawable-" folders
+ initialSize = 1260;
+ } else {
+ // "drawable-sw600dp-hdpi" etc
+ initialSize = 30;
+ }
+ }
+ break;
+ }
+ case LAYOUT: {
+ // The main layout folder has about ~185 layouts in it;
+ // the others are small
+ if (name.indexOf('-') == -1) {
+ initialSize = 200;
+ }
+ break;
+ }
+ case VALUES: {
+ if (name.indexOf('-') == -1) {
+ initialSize = 32;
+ } else {
+ initialSize = 4;
+ }
+ break;
+ }
+ case ANIM: initialSize = 85; break;
+ case COLOR: initialSize = 32; break;
+ case RAW: initialSize = 4; break;
+ default:
+ // Stick with the 16 default
+ break;
+ }
+ }
+
+ mFiles = new ArrayList(initialSize);
+ mNames = new HashMap(initialSize, 2.0f);
+ }
+
+ mFiles.add(file);
+ mNames.put(file.getFile().getName(), file);
+ }
+
+ protected void removeFile(ResourceFile file, ScanningContext context) {
+ file.dispose(context);
+ mFiles.remove(file);
+ mNames.remove(file.getFile().getName());
+ }
+
+ protected void dispose(ScanningContext context) {
+ if (mFiles != null) {
+ for (ResourceFile file : mFiles) {
+ file.dispose(context);
+ }
+
+ mFiles.clear();
+ mNames.clear();
+ }
+ }
+
+ /**
+ * Returns the {@link IAbstractFolder} associated with this object.
+ */
+ public IAbstractFolder getFolder() {
+ return mFolder;
+ }
+
+ /**
+ * Returns the {@link ResourceFolderType} of this object.
+ */
+ public ResourceFolderType getType() {
+ return mType;
+ }
+
+ public ResourceRepository getRepository() {
+ return mRepository;
+ }
+
+ /**
+ * Returns the list of {@link ResourceType}s generated by the files inside this folder.
+ */
+ public Collection getResourceTypes() {
+ ArrayList list = new ArrayList();
+
+ if (mFiles != null) {
+ for (ResourceFile file : mFiles) {
+ Collection types = file.getResourceTypes();
+
+ // loop through those and add them to the main list,
+ // if they are not already present
+ for (ResourceType resType : types) {
+ if (list.indexOf(resType) == -1) {
+ list.add(resType);
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ @Override
+ public FolderConfiguration getConfiguration() {
+ return mConfiguration;
+ }
+
+ /**
+ * Returns whether the folder contains a file with the given name.
+ * @param name the name of the file.
+ */
+ public boolean hasFile(String name) {
+ if (mNames != null && mNames.containsKey(name)) {
+ return true;
+ }
+
+ // Note: mNames.containsKey(name) is faster, but doesn't give the same result; this
+ // method seems to be called on this ResourceFolder before it has been processed,
+ // so we need to use the file system check instead:
+ return mFolder.hasFile(name);
+ }
+
+ /**
+ * Returns the {@link ResourceFile} matching a {@link IAbstractFile} object.
+ *
+ * @param file The {@link IAbstractFile} object.
+ * @param context a context object with state for the current update, such
+ * as a place to stash errors encountered
+ * @return the {@link ResourceFile} or null if no match was found.
+ */
+ private ResourceFile getFile(IAbstractFile file, ScanningContext context) {
+ assert mFolder.equals(file.getParentFolder());
+
+ if (mNames != null) {
+ ResourceFile resFile = mNames.get(file.getName());
+ if (resFile != null) {
+ return resFile;
+ }
+ }
+
+ // If the file actually exists, the resource folder may not have been
+ // scanned yet; add it lazily
+ if (file.exists()) {
+ ResourceFile resFile = createResourceFile(file);
+ resFile.load(context);
+ addFile(resFile);
+ return resFile;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the {@link ResourceFile} matching a given name.
+ * @param filename The name of the file to return.
+ * @return the {@link ResourceFile} or null
if no match was found.
+ */
+ public ResourceFile getFile(String filename) {
+ if (mNames != null) {
+ ResourceFile resFile = mNames.get(filename);
+ if (resFile != null) {
+ return resFile;
+ }
+ }
+
+ // If the file actually exists, the resource folder may not have been
+ // scanned yet; add it lazily
+ IAbstractFile file = mFolder.getFile(filename);
+ if (file != null && file.exists()) {
+ ResourceFile resFile = createResourceFile(file);
+ resFile.load(new ScanningContext());
+ addFile(resFile);
+ return resFile;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether a file in the folder is generating a resource of a specified type.
+ * @param type The {@link ResourceType} being looked up.
+ */
+ public boolean hasResources(ResourceType type) {
+ // Check if the folder type is able to generate resource of the type that was asked.
+ // this is a first check to avoid going through the files.
+ List folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+
+ boolean valid = false;
+ for (ResourceFolderType rft : folderTypes) {
+ if (rft == mType) {
+ valid = true;
+ break;
+ }
+ }
+
+ if (valid) {
+ if (mFiles != null) {
+ for (ResourceFile f : mFiles) {
+ if (f.hasResources(type)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return mFolder.toString();
+ }
+}
diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceItem.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceItem.java
new file mode 100644
index 0000000000..549e3cb323
--- /dev/null
+++ b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceItem.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2018 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 app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.resources.ResourceType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ * in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class ResourceItem implements Comparable {
+
+ private static final Comparator sComparator = new Comparator() {
+ @Override
+ public int compare(ResourceFile file1, ResourceFile file2) {
+ // get both FolderConfiguration and compare them
+ FolderConfiguration fc1 = file1.getFolder().getConfiguration();
+ FolderConfiguration fc2 = file2.getFolder().getConfiguration();
+
+ return fc1.compareTo(fc2);
+ }
+ };
+
+ private final String mName;
+
+ /**
+ * List of files generating this ResourceItem.
+ */
+ private final List mFiles = new ArrayList();
+
+ /**
+ * Constructs a new ResourceItem.
+ * @param name the name of the resource as it appears in the XML and R.java files.
+ */
+ public ResourceItem(String name) {
+ mName = name;
+ }
+
+ /**
+ * Returns the name of the resource.
+ */
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * Compares the {@link ResourceItem} to another.
+ * @param other the ResourceItem to be compared to.
+ */
+ @Override
+ public int compareTo(ResourceItem other) {
+ return mName.compareTo(other.mName);
+ }
+
+ /**
+ * Returns whether the resource is editable directly.
+ *
+ * This is typically the case for resources that don't have alternate versions, or resources
+ * of type {@link ResourceType#ID} that aren't declared inline.
+ */
+ public boolean isEditableDirectly() {
+ return hasAlternates() == false;
+ }
+
+ /**
+ * Returns whether the ID resource has been declared inline inside another resource XML file.
+ * If the resource type is not {@link ResourceType#ID}, this will always return {@code false}.
+ */
+ public boolean isDeclaredInline() {
+ return false;
+ }
+
+ /**
+ * Returns a {@link ResourceValue} for this item based on the given configuration.
+ * If the ResourceItem has several source files, one will be selected based on the config.
+ * @param type the type of the resource. This is necessary because ResourceItem doesn't embed
+ * its type, but ResourceValue does.
+ * @param referenceConfig the config of the resource item.
+ * @param isFramework whether the resource is a framework value. Same as the type.
+ * @return a ResourceValue or null if none match the config.
+ */
+ public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig,
+ boolean isFramework) {
+ // look for the best match for the given configuration
+ // the match has to be of type ResourceFile since that's what the input list contains
+ ResourceFile match = (ResourceFile) referenceConfig.findMatchingConfigurable(mFiles);
+
+ if (match != null) {
+ // get the value of this configured resource.
+ return match.getValue(type, mName);
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a new source file.
+ * @param file the source file.
+ */
+ protected void add(ResourceFile file) {
+ mFiles.add(file);
+ }
+
+ /**
+ * Removes a file from the list of source files.
+ * @param file the file to remove
+ */
+ protected void removeFile(ResourceFile file) {
+ mFiles.remove(file);
+ }
+
+ /**
+ * Returns {@code true} if the item has no source file.
+ * @return true if the item has no source file.
+ */
+ protected boolean hasNoSourceFile() {
+ return mFiles.isEmpty();
+ }
+
+ /**
+ * Reset the item by emptying its source file list.
+ */
+ protected void reset() {
+ mFiles.clear();
+ }
+
+ /**
+ * Returns the sorted list of {@link ResourceItem} objects for this resource item.
+ */
+ public ResourceFile[] getSourceFileArray() {
+ ArrayList list = new ArrayList();
+ list.addAll(mFiles);
+
+ Collections.sort(list, sComparator);
+
+ return list.toArray(new ResourceFile[0]);
+ }
+
+ /**
+ * Returns the list of source file for this resource.
+ */
+ public List getSourceFileList() {
+ return Collections.unmodifiableList(mFiles);
+ }
+
+ /**
+ * Returns if the resource has at least one non-default version.
+ *
+ * @see ResourceFile#getConfiguration()
+ * @see FolderConfiguration#isDefault()
+ */
+ public boolean hasAlternates() {
+ for (ResourceFile file : mFiles) {
+ if (file.getFolder().getConfiguration().isDefault() == false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the resource has a default version, with no qualifier.
+ *
+ * @see ResourceFile#getConfiguration()
+ * @see FolderConfiguration#isDefault()
+ */
+ public boolean hasDefault() {
+ for (ResourceFile file : mFiles) {
+ if (file.getFolder().getConfiguration().isDefault()) {
+ return true;
+ }
+ }
+
+ // We only want to return false if there's no default and more than 0 items.
+ return (mFiles.isEmpty());
+ }
+
+ /**
+ * Returns the number of alternate versions for this resource.
+ *
+ * @see ResourceFile#getConfiguration()
+ * @see FolderConfiguration#isDefault()
+ */
+ public int getAlternateCount() {
+ int count = 0;
+ for (ResourceFile file : mFiles) {
+ if (file.getFolder().getConfiguration().isDefault() == false) {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * Returns a formatted string usable in an XML to use for the {@link ResourceItem}.
+ * @param system Whether this is a system resource or a project resource.
+ * @return a string in the format @[type]/[name]
+ */
+ public String getXmlString(ResourceType type, boolean system) {
+ if (type == ResourceType.ID && isDeclaredInline()) {
+ return (system ? "@android:" : "@+") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ return (system ? "@android:" : "@") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceItem [mName=" + mName + ", mFiles=" + mFiles + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+}
diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceRepository.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceRepository.java
new file mode 100644
index 0000000000..f61f21298d
--- /dev/null
+++ b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceRepository.java
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2018 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 app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated;
+
+import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile;
+import app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder;
+import app.cash.paparazzi.deprecated.com.android.io.IAbstractResource;
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.ResourceValueMap;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.LocaleQualifier;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.resources.ResourceUrl;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import static com.android.SdkConstants.ATTR_REF_PREFIX;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.SdkConstants.RESOURCE_CLZ_ATTR;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ * in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public abstract class ResourceRepository {
+ private final IAbstractFolder mResourceFolder;
+
+ protected Map> mFolderMap =
+ new EnumMap<>(ResourceFolderType.class);
+
+ protected Map> mResourceMap =
+ new EnumMap<>(ResourceType.class);
+
+ private Map