1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.common.resources.deprecated;
18 
19 import com.android.ide.common.rendering.api.DensityBasedResourceValueImpl;
20 import com.android.ide.common.rendering.api.ResourceNamespace;
21 import com.android.ide.common.rendering.api.ResourceReference;
22 import com.android.ide.common.rendering.api.ResourceValue;
23 import com.android.ide.common.rendering.api.ResourceValueImpl;
24 import com.android.ide.common.resources.ResourceValueMap;
25 import com.android.ide.common.resources.configuration.DensityQualifier;
26 import com.android.ide.common.resources.configuration.ResourceQualifier;
27 import com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository;
28 import com.android.io.IAbstractFile;
29 import com.android.io.StreamException;
30 import com.android.resources.ResourceType;
31 
32 import java.io.IOException;
33 import java.util.Collection;
34 import java.util.EnumSet;
35 import java.util.HashSet;
36 import java.util.Set;
37 
38 /**
39  * @deprecated This class is part of an obsolete resource repository system that is no longer used
40  *     in production code. The class is preserved temporarily for LayoutLib tests.
41  */
42 @Deprecated
43 public final class IdGeneratingResourceFile extends ResourceFile
44         implements IValueResourceRepository {
45 
46     private final ResourceValueMap mIdResources = ResourceValueMap.create();
47 
48     private final Collection<ResourceType> mResourceTypeList;
49 
50     private final String mFileName;
51 
52     private final ResourceType mFileType;
53 
54     private final ResourceValue mFileValue;
55 
IdGeneratingResourceFile(TestFileWrapper file, ResourceFolder folder, ResourceType type)56     public IdGeneratingResourceFile(TestFileWrapper file, ResourceFolder folder, ResourceType type) {
57         super(file, folder);
58 
59         mFileType = type;
60 
61         // Set up our resource types
62         mResourceTypeList = EnumSet.of(mFileType, ResourceType.ID);
63 
64         // compute the resource name
65         mFileName = getFileName();
66 
67         // Get the resource value of this file as a whole layout
68         mFileValue = getFileValue(file, folder);
69     }
70 
71     @Override
load(ScanningContext context)72     protected void load(ScanningContext context) {
73         // Parse the file and look for @+id/ entries
74         parseFileForIds();
75 
76         // create the resource items in the repository
77         updateResourceItems(context);
78     }
79 
80     @Override
update(ScanningContext context)81     protected void update(ScanningContext context) {
82         // Copy the previous list of ID names
83         Set<String> oldIdNames = new HashSet<>(mIdResources.keySet());
84 
85         // reset current content.
86         mIdResources.clear();
87 
88         // need to parse the file and find the IDs.
89         if (!parseFileForIds()) {
90             context.requestFullAapt();
91             // Continue through to updating the resource item here since it
92             // will make for example layout rendering more accurate until
93             // aapt is re-run
94         }
95 
96         // We only need to update the repository if our IDs have changed
97         Set<String> keySet = mIdResources.keySet();
98         assert keySet != oldIdNames;
99         if (!oldIdNames.equals(keySet)) {
100             updateResourceItems(context);
101         }
102     }
103 
104     @Override
getValue(ResourceType type, String name)105     public ResourceValue getValue(ResourceType type, String name) {
106         // Check to see if they're asking for one of the right types:
107         if (type != mFileType && type != ResourceType.ID) {
108             return null;
109         }
110 
111         // If they're looking for a resource of this type with this name give them the whole file
112         if (type == mFileType && name.equals(mFileName)) {
113             return mFileValue;
114         } else {
115             // Otherwise try to return them an ID
116             // the map will return null if it's not found
117             return mIdResources.get(name);
118         }
119     }
120 
121     /**
122      * Looks through the file represented for Ids and adds them to
123      * our id repository
124      *
125      * @return true if parsing succeeds and false if it fails
126      */
parseFileForIds()127     private boolean parseFileForIds() {
128         IdResourceParser parser = new IdResourceParser(this, isFramework());
129         try {
130             IAbstractFile file = getFile();
131             return parser.parse(file.getContents());
132         } catch (IOException | StreamException ignore) {}
133 
134         return false;
135     }
136 
137     /**
138      * Add the resources represented by this file to the repository
139      */
updateResourceItems(ScanningContext context)140     private void updateResourceItems(ScanningContext context) {
141         ResourceRepository repository = getRepository();
142 
143         // remove this file from all existing ResourceItem.
144         repository.removeFile(mResourceTypeList, this);
145 
146         // First add this as a layout file
147         ResourceItem item = repository.getResourceItem(mFileType, mFileName);
148         item.add(this);
149 
150         // Now iterate through our IDs and add
151         for (String idName : mIdResources.keySet()) {
152             item = repository.getResourceItem(ResourceType.ID, idName);
153             // add this file to the list of files generating ID resources.
154             item.add(this);
155         }
156 
157         //  Ask the repository for an ID refresh
158         context.requestFullAapt();
159     }
160 
161     /**
162      * Returns the resource value associated with this whole file as a layout resource
163      * @param file the file handler that represents this file
164      * @param folder the folder this file is under
165      * @return a resource value associated with this layout
166      */
getFileValue(IAbstractFile file, ResourceFolder folder)167     private ResourceValue getFileValue(IAbstractFile file, ResourceFolder folder) {
168         // test if there's a density qualifier associated with the resource
169         DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier();
170 
171         ResourceValue value;
172         if (!ResourceQualifier.isValid(qualifier)) {
173             value =
174                     new ResourceValueImpl(
175                             new ResourceReference(
176                                     ResourceNamespace.fromBoolean(isFramework()),
177                                     mFileType,
178                                     mFileName),
179                             file.getOsLocation());
180         } else {
181             value =
182                     new DensityBasedResourceValueImpl(
183                             new ResourceReference(
184                                     ResourceNamespace.fromBoolean(isFramework()),
185                                     mFileType,
186                                     mFileName),
187                             file.getOsLocation(),
188                             qualifier.getValue());
189         }
190         return value;
191     }
192 
193 
194     /**
195      * Returns the name of this resource.
196      */
getFileName()197     private String getFileName() {
198         // get the name from the filename.
199         String name = getFile().getName();
200 
201         int pos = name.indexOf('.');
202         if (pos != -1) {
203             name = name.substring(0, pos);
204         }
205 
206         return name;
207     }
208 
209     @Override
addResourceValue(ResourceValue value)210     public void addResourceValue(ResourceValue value) {
211         // Just overwrite collisions. We're only interested in the unique
212         // IDs declared
213         mIdResources.put(value.getName(), value);
214     }
215 }
216