001/** 002 * Copyright 2013 John Ericksen 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.androidtransfuse.processor; 017 018import org.androidtransfuse.TransfuseAnalysisException; 019import org.androidtransfuse.config.TransfuseAndroidModule; 020import org.androidtransfuse.model.Mergeable; 021import org.androidtransfuse.model.manifest.*; 022import org.androidtransfuse.util.AndroidLiterals; 023import org.apache.commons.beanutils.PropertyUtils; 024 025import javax.inject.Inject; 026import javax.inject.Named; 027import javax.inject.Singleton; 028import java.beans.BeanInfo; 029import java.beans.IntrospectionException; 030import java.beans.Introspector; 031import java.beans.PropertyDescriptor; 032import java.lang.annotation.Annotation; 033import java.lang.reflect.InvocationTargetException; 034import java.lang.reflect.Method; 035import java.util.ArrayList; 036import java.util.List; 037 038/** 039 * @author John Ericksen 040 */ 041@Singleton 042public class ManifestManager { 043 044 private final List<Application> applications = new ArrayList<Application>(); 045 private final String manifestPackage; 046 private final List<Activity> activities = new ArrayList<Activity>(); 047 private final List<Receiver> broadcastReceivers = new ArrayList<Receiver>(); 048 private final List<Service> services = new ArrayList<Service>(); 049 private final List<UsesPermission> usesPermissions = new ArrayList<UsesPermission>(); 050 private final List<UsesFeature> usesFeatures = new ArrayList<UsesFeature>(); 051 private final List<Permission> permissions = new ArrayList<Permission>(); 052 private UsesSDK usesSdk; 053 054 @Inject 055 public ManifestManager(@Named(TransfuseAndroidModule.ORIGINAL_MANIFEST) Manifest originalManifest) { 056 this.manifestPackage = originalManifest.getApplicationPackage(); 057 } 058 059 public void addApplication(Application application) { 060 this.applications.add(application); 061 } 062 063 public void addPermission(Permission permission){ 064 try { 065 updateMergeTags(Permission.class, permission); 066 permissions.add(permission); 067 } catch (MergerException e) { 068 throw new TransfuseAnalysisException("Unable to Merge UsesPermission", e); 069 } 070 } 071 072 public void addUsesFeature(UsesFeature usesFeature){ 073 try { 074 updateMergeTags(UsesFeature.class, usesFeature); 075 usesFeatures.add(usesFeature); 076 } catch (MergerException e) { 077 throw new TransfuseAnalysisException("Unable to Merge UsesFeature", e); 078 } 079 } 080 081 public void addUsesPermission(UsesPermission usesPermission) { 082 try { 083 updateMergeTags(UsesPermission.class, usesPermission); 084 usesPermissions.add(usesPermission); 085 } catch (MergerException e) { 086 throw new TransfuseAnalysisException("Unable to Merge UsesPermission", e); 087 } 088 } 089 090 public void setUsesSdk(UsesSDK usesSdk) { 091 this.usesSdk = usesSdk; 092 } 093 094 public void addActivity(Activity activity) { 095 try { 096 updateMergeTags(Activity.class, activity); 097 updateMergeTags(IntentFilter.class, activity.getIntentFilters()); 098 updateMergeTags(MetaData.class, activity.getMetaData()); 099 this.activities.add(activity); 100 } catch (MergerException e) { 101 throw new TransfuseAnalysisException("Unable to Merge Activity", e); 102 } 103 } 104 105 public void addBroadcastReceiver(Receiver broadcastReceiver) { 106 try { 107 updateMergeTags(Receiver.class, broadcastReceiver); 108 updateMergeTags(IntentFilter.class, broadcastReceiver.getIntentFilters()); 109 updateMergeTags(MetaData.class, broadcastReceiver.getMetaData()); 110 this.broadcastReceivers.add(broadcastReceiver); 111 } catch (MergerException e) { 112 throw new TransfuseAnalysisException("Unable to Merge Broadcast Receiver", e); 113 } 114 } 115 116 private <T extends Mergeable> void updateMergeTags(Class<T> clazz, List<T> mergeableCollection) throws MergerException { 117 for (T mergeable : mergeableCollection) { 118 updateMergeTags(clazz, mergeable); 119 } 120 } 121 122 public void addService(Service service) { 123 try { 124 updateMergeTags(Service.class, service); 125 updateMergeTags(IntentFilter.class, service.getIntentFilters()); 126 updateMergeTags(MetaData.class, service.getMetaData()); 127 this.services.add(service); 128 } catch (MergerException e) { 129 throw new TransfuseAnalysisException("Unable to Merge Service", e); 130 } 131 } 132 133 public Manifest getManifest() throws MergerException { 134 Manifest manifest = new Manifest(); 135 manifest.setApplicationPackage(manifestPackage); 136 137 Application localApplication; 138 139 if (applications.isEmpty()){ 140 localApplication = new Application(); 141 localApplication.setName(AndroidLiterals.APPLICATION.getName()); 142 } 143 else{ 144 localApplication = applications.get(0); 145 } 146 147 //todo: do multiple applications result in an error? 148 149 updateMergeTags(Application.class, localApplication); 150 151 localApplication.getActivities().addAll(activities); 152 localApplication.getReceivers().addAll(broadcastReceivers); 153 localApplication.getServices().addAll(services); 154 155 manifest.getApplications().add(localApplication); 156 157 manifest.getUsesPermissions().addAll(usesPermissions); 158 159 manifest.getUsesFeatures().addAll(usesFeatures); 160 161 manifest.getPermissions().addAll(permissions); 162 163 if(usesSdk != null){ 164 manifest.getUsesSDKs().add(usesSdk); 165 } 166 167 manifest.updatePackages(); 168 169 return manifest; 170 } 171 172 private <T extends Mergeable> void updateMergeTags(Class<T> clazz, T mergeable) throws MergerException { 173 try { 174 mergeable.setGenerated(true); 175 176 BeanInfo beanInfo = Introspector.getBeanInfo(clazz); 177 178 for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { 179 Method readMethod = propertyDescriptor.getReadMethod(); 180 Method writeMethod = propertyDescriptor.getWriteMethod(); 181 182 Merge mergeAnnotation = findAnnotation(Merge.class, writeMethod, readMethod); 183 Object property = PropertyUtils.getProperty(mergeable, propertyDescriptor.getName()); 184 185 if (mergeAnnotation != null && property != null) { 186 mergeable.addMergeTag(mergeAnnotation.value()); 187 } 188 } 189 } catch (IntrospectionException e) { 190 throw new MergerException(e); 191 } catch (InvocationTargetException e) { 192 throw new MergerException(e); 193 } catch (NoSuchMethodException e) { 194 throw new MergerException(e); 195 } catch (IllegalAccessException e) { 196 throw new MergerException(e); 197 } 198 } 199 200 private <T extends Annotation> T findAnnotation(Class<T> annotationClass, Method... methods) { 201 T annotation = null; 202 if (methods != null) { 203 for (Method method : methods) { 204 if (annotation == null && method != null && method.isAnnotationPresent(annotationClass)) { 205 annotation = method.getAnnotation(annotationClass); 206 } 207 } 208 } 209 return annotation; 210 } 211}