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