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