• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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;
18 
19 import com.android.ide.common.rendering.api.ResourceValue;
20 import com.android.ide.common.resources.ValueResourceParser.IValueResourceRepository;
21 import com.android.io.IAbstractFile;
22 import com.android.io.StreamException;
23 import com.android.resources.ResourceType;
24 
25 import org.xml.sax.SAXException;
26 
27 import java.io.IOException;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.EnumMap;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 import javax.xml.parsers.ParserConfigurationException;
35 import javax.xml.parsers.SAXParser;
36 import javax.xml.parsers.SAXParserFactory;
37 
38 /**
39  * Represents a resource file able to declare multiple resources, which could be of
40  * different {@link ResourceType}.
41  * <p/>
42  * This is typically an XML file inside res/values.
43  */
44 public final class MultiResourceFile extends ResourceFile implements IValueResourceRepository {
45 
46     private final static SAXParserFactory sParserFactory = SAXParserFactory.newInstance();
47 
48     private final Map<ResourceType, Map<String, ResourceValue>> mResourceItems =
49         new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
50 
51     private Collection<ResourceType> mResourceTypeList = null;
52 
MultiResourceFile(IAbstractFile file, ResourceFolder folder)53     public MultiResourceFile(IAbstractFile file, ResourceFolder folder) {
54         super(file, folder);
55     }
56 
57     // Boolean flag to track whether a named element has been added or removed, thus requiring
58     // a new ID table to be generated
59     private boolean mNeedIdRefresh;
60 
61     @Override
load(ScanningContext context)62     protected void load(ScanningContext context) {
63         // need to parse the file and find the content.
64         parseFile();
65 
66         // create new ResourceItems for the new content.
67         mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
68 
69         // We need an ID generation step
70         mNeedIdRefresh = true;
71 
72         // create/update the resource items.
73         updateResourceItems(context);
74     }
75 
76     @Override
update(ScanningContext context)77     protected void update(ScanningContext context) {
78         // Reset the ID generation flag
79         mNeedIdRefresh = false;
80 
81         // Copy the previous version of our list of ResourceItems and types
82         Map<ResourceType, Map<String, ResourceValue>> oldResourceItems
83                         = new EnumMap<ResourceType, Map<String, ResourceValue>>(mResourceItems);
84 
85         // reset current content.
86         mResourceItems.clear();
87 
88         // need to parse the file and find the content.
89         parseFile();
90 
91         // create new ResourceItems for the new content.
92         mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
93 
94         // Check to see if any names have changed. If so, mark the flag so updateResourceItems
95         // can notify the ResourceRepository that an ID refresh is needed
96         if (oldResourceItems.keySet().equals(mResourceItems.keySet())) {
97             for (ResourceType type : mResourceTypeList) {
98                 // We just need to check the names of the items.
99                 // If there are new or removed names then we'll have to regenerate IDs
100                 if (mResourceItems.get(type).keySet()
101                                           .equals(oldResourceItems.get(type).keySet()) == false) {
102                     mNeedIdRefresh = true;
103                 }
104             }
105         } else {
106             // If our type list is different, obviously the names will be different
107             mNeedIdRefresh = true;
108         }
109         // create/update the resource items.
110         updateResourceItems(context);
111     }
112 
113     @Override
dispose(ScanningContext context)114     protected void dispose(ScanningContext context) {
115         ResourceRepository repository = getRepository();
116 
117         // only remove this file from all existing ResourceItem.
118         repository.removeFile(mResourceTypeList, this);
119 
120         // We'll need an ID refresh because we deleted items
121         context.requestFullAapt();
122 
123         // don't need to touch the content, it'll get reclaimed as this objects disappear.
124         // In the mean time other objects may need to access it.
125     }
126 
127     @Override
getResourceTypes()128     public Collection<ResourceType> getResourceTypes() {
129         return mResourceTypeList;
130     }
131 
132     @Override
hasResources(ResourceType type)133     public boolean hasResources(ResourceType type) {
134         Map<String, ResourceValue> list = mResourceItems.get(type);
135         return (list != null && list.size() > 0);
136     }
137 
updateResourceItems(ScanningContext context)138     private void updateResourceItems(ScanningContext context) {
139         ResourceRepository repository = getRepository();
140 
141         // remove this file from all existing ResourceItem.
142         repository.removeFile(mResourceTypeList, this);
143 
144         for (ResourceType type : mResourceTypeList) {
145             Map<String, ResourceValue> list = mResourceItems.get(type);
146 
147             if (list != null) {
148                 Collection<ResourceValue> values = list.values();
149                 for (ResourceValue res : values) {
150                     ResourceItem item = repository.getResourceItem(type, res.getName());
151 
152                     // add this file to the list of files generating this resource item.
153                     item.add(this);
154                 }
155             }
156         }
157 
158         // If we need an ID refresh, ask the repository for that now
159         if (mNeedIdRefresh) {
160             context.requestFullAapt();
161         }
162     }
163 
164     /**
165      * Parses the file and creates a list of {@link ResourceType}.
166      */
parseFile()167     private void parseFile() {
168         try {
169             SAXParser parser = sParserFactory.newSAXParser();
170             parser.parse(getFile().getContents(), new ValueResourceParser(this, isFramework()));
171         } catch (ParserConfigurationException e) {
172         } catch (SAXException e) {
173         } catch (IOException e) {
174         } catch (StreamException e) {
175         }
176     }
177 
178     /**
179      * Adds a resource item to the list
180      * @param value The value of the resource.
181      */
182     @Override
addResourceValue(ResourceValue value)183     public void addResourceValue(ResourceValue value) {
184         ResourceType resType = value.getResourceType();
185 
186         Map<String, ResourceValue> list = mResourceItems.get(resType);
187 
188         // if the list does not exist, create it.
189         if (list == null) {
190             list = new HashMap<String, ResourceValue>();
191             mResourceItems.put(resType, list);
192         } else {
193             // look for a possible value already existing.
194             ResourceValue oldValue = list.get(value.getName());
195 
196             if (oldValue != null) {
197                 oldValue.replaceWith(value);
198                 return;
199             }
200         }
201 
202         // empty list or no match found? add the given resource
203         list.put(value.getName(), value);
204     }
205 
206     @Override
hasResourceValue(ResourceType type, String name)207     public boolean hasResourceValue(ResourceType type, String name) {
208         Map<String, ResourceValue> map = mResourceItems.get(type);
209         return map != null && map.containsKey(name);
210     }
211 
212     @Override
getValue(ResourceType type, String name)213     public ResourceValue getValue(ResourceType type, String name) {
214         // get the list for the given type
215         Map<String, ResourceValue> list = mResourceItems.get(type);
216 
217         if (list != null) {
218             return list.get(name);
219         }
220 
221         return null;
222     }
223 }
224