• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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.eclipse.adt.internal.resources.manager;
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.AndroidConstants;
21 import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser;
22 import com.android.ide.eclipse.adt.internal.resources.ResourceType;
23 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor.IFileListener;
24 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor.IProjectListener;
25 
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IMarkerDelta;
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.resources.IResourceDelta;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IStatus;
33 
34 import java.lang.reflect.Field;
35 import java.lang.reflect.Modifier;
36 import java.util.HashMap;
37 import java.util.Map;
38 
39 /**
40  * A monitor for the compiled resources. This only monitors changes in the resources of type
41  *  {@link ResourceType#ID}.
42  */
43 public final class CompiledResourcesMonitor implements IFileListener, IProjectListener {
44 
45     private final static CompiledResourcesMonitor sThis = new CompiledResourcesMonitor();
46 
47     /**
48      * Sets up the monitoring system.
49      * @param monitor The main Resource Monitor.
50      */
setupMonitor(ResourceMonitor monitor)51     public static void setupMonitor(ResourceMonitor monitor) {
52         monitor.addFileListener(sThis, IResourceDelta.ADDED | IResourceDelta.CHANGED);
53         monitor.addProjectListener(sThis);
54     }
55 
56     /**
57      * private constructor to prevent construction.
58      */
CompiledResourcesMonitor()59     private CompiledResourcesMonitor() {
60     }
61 
62 
63     /* (non-Javadoc)
64      * Sent when a file changed : if the file is the R class, then it is parsed again to update
65      * the internal data.
66      *
67      * @param file The file that changed.
68      * @param markerDeltas The marker deltas for the file.
69      * @param kind The change kind. This is equivalent to
70      * {@link IResourceDelta#accept(IResourceDeltaVisitor)}
71      *
72      * @see IFileListener#fileChanged
73      */
fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind)74     public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
75         if (file.getName().equals(AndroidConstants.FN_COMPILED_RESOURCE_CLASS)) {
76             loadAndParseRClass(file.getProject());
77         }
78     }
79 
80     /**
81      * Processes project close event.
82      */
projectClosed(IProject project)83     public void projectClosed(IProject project) {
84         // the ProjectResources object will be removed by the ResourceManager.
85     }
86 
87     /**
88      * Processes project delete event.
89      */
projectDeleted(IProject project)90     public void projectDeleted(IProject project) {
91         // the ProjectResources object will be removed by the ResourceManager.
92     }
93 
94     /**
95      * Processes project open event.
96      */
projectOpened(IProject project)97     public void projectOpened(IProject project) {
98         // when the project is opened, we get an ADDED event for each file, so we don't
99         // need to do anything here.
100     }
101 
102     /**
103      * Processes existing project at init time.
104      */
projectOpenedWithWorkspace(IProject project)105     public void projectOpenedWithWorkspace(IProject project) {
106         try {
107             // check this is an android project
108             if (project.hasNature(AndroidConstants.NATURE)) {
109                 loadAndParseRClass(project);
110             }
111         } catch (CoreException e) {
112             // pass
113         }
114     }
115 
loadAndParseRClass(IProject project)116     private void loadAndParseRClass(IProject project) {
117         try {
118             // first check there's a ProjectResources to store the content
119             ProjectResources projectResources = ResourceManager.getInstance().getProjectResources(
120                     project);
121 
122             if (projectResources != null) {
123                 // create the classname
124                 String className = getRClassName(project);
125                 if (className == null) {
126                     // We need to abort.
127                     AdtPlugin.log(IStatus.ERROR,
128                             "loadAndParseRClass: failed to find manifest package for project %1$s", //$NON-NLS-1$
129                             project.getName());
130                     return;
131                 }
132 
133                 // create a temporary class loader to load it.
134                 ProjectClassLoader loader = new ProjectClassLoader(null /* parentClassLoader */,
135                         project);
136 
137                 try {
138                     Class<?> clazz = loader.loadClass(className);
139 
140                     if (clazz != null) {
141                         // create the maps to store the result of the parsing
142                         Map<String, Map<String, Integer>> resourceValueMap =
143                             new HashMap<String, Map<String, Integer>>();
144                         Map<Integer, String[]> genericValueToNameMap =
145                             new HashMap<Integer, String[]>();
146                         Map<IntArrayWrapper, String> styleableValueToNameMap =
147                             new HashMap<IntArrayWrapper, String>();
148 
149                         // parse the class
150                         if (parseClass(clazz, genericValueToNameMap, styleableValueToNameMap,
151                                 resourceValueMap)) {
152                             // now we associate the maps to the project.
153                             projectResources.setCompiledResources(genericValueToNameMap,
154                                     styleableValueToNameMap, resourceValueMap);
155                         }
156                     }
157                 } catch (Error e) {
158                     // Log this error with the class name we're trying to load and abort.
159                     AdtPlugin.log(e, "loadAndParseRClass failed to find class %1$s", className); //$NON-NLS-1$
160                 }
161             }
162         } catch (ClassNotFoundException e) {
163             // pass
164         }
165     }
166 
167     /**
168      * Parses a R class, and fills maps.
169      * @param rClass the class to parse
170      * @param genericValueToNameMap
171      * @param styleableValueToNameMap
172      * @param resourceValueMap
173      * @return True if we managed to parse the R class.
174      */
parseClass(Class<?> rClass, Map<Integer, String[]> genericValueToNameMap, Map<IntArrayWrapper, String> styleableValueToNameMap, Map<String, Map<String, Integer>> resourceValueMap)175     private boolean parseClass(Class<?> rClass, Map<Integer, String[]> genericValueToNameMap,
176             Map<IntArrayWrapper, String> styleableValueToNameMap, Map<String,
177             Map<String, Integer>> resourceValueMap) {
178         try {
179             for (Class<?> inner : rClass.getDeclaredClasses()) {
180                 String resType = inner.getSimpleName();
181 
182                 Map<String, Integer> fullMap = new HashMap<String, Integer>();
183                 resourceValueMap.put(resType, fullMap);
184 
185                 for (Field f : inner.getDeclaredFields()) {
186                     // only process static final fields.
187                     int modifiers = f.getModifiers();
188                     if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
189                         Class<?> type = f.getType();
190                         if (type.isArray() && type.getComponentType() == int.class) {
191                             // if the object is an int[] we put it in the styleable map
192                             styleableValueToNameMap.put(new IntArrayWrapper((int[]) f.get(null)),
193                                     f.getName());
194                         } else if (type == int.class) {
195                             Integer value = (Integer) f.get(null);
196                             genericValueToNameMap.put(value, new String[] { f.getName(), resType });
197                             fullMap.put(f.getName(), value);
198                         } else {
199                             assert false;
200                         }
201                     }
202                 }
203             }
204 
205             return true;
206         } catch (IllegalArgumentException e) {
207         } catch (IllegalAccessException e) {
208         }
209         return false;
210     }
211 
212     /**
213      * Returns the class name of the R class, based on the project's manifest's package.
214      *
215      * @return A class name (e.g. "my.app.R") or null if there's no valid package in the manifest.
216      */
getRClassName(IProject project)217     private String getRClassName(IProject project) {
218         try {
219             IFile manifestFile = AndroidManifestParser.getManifest(project);
220             if (manifestFile != null && manifestFile.isSynchronized(IResource.DEPTH_ZERO)) {
221                 AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile);
222                 if (data != null) {
223                     String javaPackage = data.getPackage();
224                     return javaPackage + ".R"; //$NON-NLS-1$
225                 }
226             }
227         } catch (CoreException e) {
228             // This will typically happen either because the manifest file is not present
229             // and/or the workspace needs to be refreshed.
230             AdtPlugin.logAndPrintError(e,
231                     "Android Resources",
232                     "Failed to find the package of the AndroidManifest of project %1$s. Reason: %2$s",
233                     project.getName(),
234                     e.getMessage());
235         }
236         return null;
237     }
238 
239 }
240