1 /* 2 * Copyright (C) 2008 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.editors.layout; 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.manager.ProjectClassLoader; 23 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; 24 import com.android.layoutlib.api.IProjectCallback; 25 26 import org.eclipse.core.resources.IFile; 27 import org.eclipse.core.resources.IProject; 28 import org.eclipse.core.runtime.CoreException; 29 30 import java.lang.reflect.Constructor; 31 import java.util.HashMap; 32 33 /** 34 * Loader for Android Project class in order to use them in the layout editor. 35 */ 36 public final class ProjectCallback implements IProjectCallback { 37 38 private final HashMap<String, Class<?>> mLoadedClasses = new HashMap<String, Class<?>>(); 39 private final IProject mProject; 40 private final ClassLoader mParentClassLoader; 41 private final ProjectResources mProjectRes; 42 private boolean mUsed = false; 43 private String mNamespace; 44 ProjectCallback(ClassLoader classLoader, ProjectResources projectRes, IProject project)45 ProjectCallback(ClassLoader classLoader, ProjectResources projectRes, IProject project) { 46 mParentClassLoader = classLoader; 47 mProjectRes = projectRes; 48 mProject = project; 49 } 50 51 52 /** 53 * {@inheritDoc} 54 * 55 * This implementation goes through the output directory of the Eclipse project and loads the 56 * <code>.class</code> file directly. 57 */ 58 @SuppressWarnings("unchecked") loadView(String className, Class[] constructorSignature, Object[] constructorParameters)59 public Object loadView(String className, Class[] constructorSignature, 60 Object[] constructorParameters) 61 throws ClassNotFoundException, Exception { 62 63 // look for a cached version 64 Class<?> clazz = mLoadedClasses.get(className); 65 if (clazz != null) { 66 return instantiateClass(clazz, constructorSignature, constructorParameters); 67 } 68 69 // load the class. 70 ProjectClassLoader loader = new ProjectClassLoader(mParentClassLoader, mProject); 71 try { 72 clazz = loader.loadClass(className); 73 74 if (clazz != null) { 75 mUsed = true; 76 mLoadedClasses.put(className, clazz); 77 return instantiateClass(clazz, constructorSignature, constructorParameters); 78 } 79 } catch (Error e) { 80 // Log this error with the class name we're trying to load and abort. 81 AdtPlugin.log(e, "ProjectCallback.loadView failed to find class %1$s", className); //$NON-NLS-1$ 82 } 83 84 return null; 85 } 86 87 /** 88 * Returns the namespace for the project. The namespace contains a standard part + the 89 * application package. 90 * 91 * @return The package namespace of the project or null in case of error. 92 */ getNamespace()93 public String getNamespace() { 94 if (mNamespace == null) { 95 IFile manifestFile = AndroidManifestParser.getManifest(mProject); 96 try { 97 AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile); 98 String javaPackage = data.getPackage(); 99 mNamespace = String.format(AndroidConstants.NS_CUSTOM_RESOURCES, javaPackage); 100 } catch (CoreException e) { 101 } 102 } 103 104 return mNamespace; 105 } 106 107 /* 108 * (non-Javadoc) 109 * @see com.android.layoutlib.api.IProjectCallback#resolveResourceValue(int) 110 */ resolveResourceValue(int id)111 public String[] resolveResourceValue(int id) { 112 if (mProjectRes != null) { 113 return mProjectRes.resolveResourceValue(id); 114 } 115 116 return null; 117 } 118 119 /* 120 * (non-Javadoc) 121 * @see com.android.layoutlib.api.IProjectCallback#resolveResourceValue(int[]) 122 */ resolveResourceValue(int[] id)123 public String resolveResourceValue(int[] id) { 124 if (mProjectRes != null) { 125 return mProjectRes.resolveResourceValue(id); 126 } 127 128 return null; 129 } 130 131 /* 132 * (non-Javadoc) 133 * @see com.android.layoutlib.api.IProjectCallback#getResourceValue(java.lang.String, java.lang.String) 134 */ getResourceValue(String type, String name)135 public Integer getResourceValue(String type, String name) { 136 if (mProjectRes != null) { 137 return mProjectRes.getResourceValue(type, name); 138 } 139 140 return null; 141 } 142 143 /** 144 * Returns whether the loader has received requests to load custom views. 145 * <p/>This allows to efficiently only recreate when needed upon code change in the project. 146 */ isUsed()147 boolean isUsed() { 148 return mUsed; 149 } 150 151 /** 152 * Instantiate a class object, using a specific constructor and parameters. 153 * @param clazz the class to instantiate 154 * @param constructorSignature the signature of the constructor to use 155 * @param constructorParameters the parameters to use in the constructor. 156 * @return A new class object, created using a specific constructor and parameters. 157 * @throws Exception 158 */ 159 @SuppressWarnings("unchecked") instantiateClass(Class<?> clazz, Class[] constructorSignature, Object[] constructorParameters)160 private Object instantiateClass(Class<?> clazz, Class[] constructorSignature, 161 Object[] constructorParameters) throws Exception { 162 Constructor<?> constructor = clazz.getConstructor(constructorSignature); 163 constructor.setAccessible(true); 164 return constructor.newInstance(constructorParameters); 165 } 166 } 167