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
17package com.android.ide.eclipse.adt.internal.sdk;
18
19import com.android.annotations.NonNull;
20import com.android.annotations.Nullable;
21import com.android.ide.common.rendering.LayoutLibrary;
22import com.android.ide.common.rendering.api.LayoutLog;
23import com.android.ide.common.resources.ResourceRepository;
24import com.android.ide.common.resources.platform.AttributeInfo;
25import com.android.ide.common.sdk.LoadStatus;
26import com.android.ide.eclipse.adt.AdtPlugin;
27import com.android.ide.eclipse.adt.internal.editors.animator.AnimDescriptors;
28import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors;
29import com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors;
30import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
31import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableDescriptors;
32import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
33import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
34import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors;
35import com.android.ide.eclipse.adt.internal.editors.otherxml.descriptors.OtherXmlDescriptors;
36import com.android.ide.eclipse.adt.internal.editors.values.descriptors.ValuesDescriptors;
37import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
38import com.android.sdklib.IAndroidTarget;
39import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
40
41import org.eclipse.core.runtime.IStatus;
42
43import java.io.File;
44import java.util.ArrayList;
45import java.util.Hashtable;
46import java.util.Map;
47
48/**
49 * This class contains the data of an Android Target as loaded from the SDK.
50 */
51public class AndroidTargetData {
52
53    public final static int DESCRIPTOR_MANIFEST = 1;
54    public final static int DESCRIPTOR_LAYOUT = 2;
55    public final static int DESCRIPTOR_MENU = 3;
56    public final static int DESCRIPTOR_OTHER_XML = 4;
57    public final static int DESCRIPTOR_RESOURCES = 5;
58    public final static int DESCRIPTOR_SEARCHABLE = 6;
59    public final static int DESCRIPTOR_PREFERENCES = 7;
60    public final static int DESCRIPTOR_APPWIDGET_PROVIDER = 8;
61    public final static int DESCRIPTOR_DRAWABLE = 9;
62    public final static int DESCRIPTOR_ANIMATOR = 10;
63    public final static int DESCRIPTOR_ANIM = 11;
64    public final static int DESCRIPTOR_COLOR = 12;
65
66    private final IAndroidTarget mTarget;
67
68    /**
69     * mAttributeValues is a map { key => list [ values ] }.
70     * The key for the map is "(element-xml-name,attribute-namespace:attribute-xml-local-name)".
71     * The attribute namespace prefix must be:
72     * - "android" for SdkConstants.NS_RESOURCES
73     * - "xmlns" for the XMLNS URI.
74     *
75     * This is used for attributes that do not have a unique name, but still need to be populated
76     * with values in the UI. Uniquely named attributes have their values in {@link #mEnumValueMap}.
77     */
78    private Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>();
79
80    private AndroidManifestDescriptors mManifestDescriptors;
81    private DrawableDescriptors mDrawableDescriptors;
82    private AnimatorDescriptors mAnimatorDescriptors;
83    private AnimDescriptors mAnimDescriptors;
84    private ColorDescriptors mColorDescriptors;
85    private LayoutDescriptors mLayoutDescriptors;
86    private MenuDescriptors mMenuDescriptors;
87    private OtherXmlDescriptors mOtherXmlDescriptors;
88
89    private Map<String, Map<String, Integer>> mEnumValueMap;
90
91    private ResourceRepository mFrameworkResources;
92    private LayoutLibrary mLayoutLibrary;
93    private Map<String, AttributeInfo> mAttributeMap;
94
95    private boolean mLayoutBridgeInit = false;
96
97    AndroidTargetData(IAndroidTarget androidTarget) {
98        mTarget = androidTarget;
99    }
100
101    /**
102     * Sets the associated map from string attribute name to
103     * {@link AttributeInfo}
104     *
105     * @param attributeMap the map
106     */
107    public void setAttributeMap(@NonNull Map<String, AttributeInfo> attributeMap) {
108        mAttributeMap = attributeMap;
109    }
110
111    /**
112     * Returns the associated map from string attribute name to
113     * {@link AttributeInfo}
114     *
115     * @return the map
116     */
117    @Nullable
118    public Map<String, AttributeInfo> getAttributeMap() {
119        return mAttributeMap;
120    }
121
122    /**
123     * Creates an AndroidTargetData object.
124     */
125    void setExtraData(
126            AndroidManifestDescriptors manifestDescriptors,
127            LayoutDescriptors layoutDescriptors,
128            MenuDescriptors menuDescriptors,
129            OtherXmlDescriptors otherXmlDescriptors,
130            DrawableDescriptors drawableDescriptors,
131            AnimatorDescriptors animatorDescriptors,
132            AnimDescriptors animDescriptors,
133            ColorDescriptors colorDescriptors,
134            Map<String, Map<String, Integer>> enumValueMap,
135            String[] permissionValues,
136            String[] activityIntentActionValues,
137            String[] broadcastIntentActionValues,
138            String[] serviceIntentActionValues,
139            String[] intentCategoryValues,
140            String[] platformLibraries,
141            IOptionalLibrary[] optionalLibraries,
142            ResourceRepository frameworkResources,
143            LayoutLibrary layoutLibrary) {
144
145        mManifestDescriptors = manifestDescriptors;
146        mDrawableDescriptors = drawableDescriptors;
147        mAnimatorDescriptors = animatorDescriptors;
148        mAnimDescriptors = animDescriptors;
149        mColorDescriptors = colorDescriptors;
150        mLayoutDescriptors = layoutDescriptors;
151        mMenuDescriptors = menuDescriptors;
152        mOtherXmlDescriptors = otherXmlDescriptors;
153        mEnumValueMap = enumValueMap;
154        mFrameworkResources = frameworkResources;
155        mLayoutLibrary = layoutLibrary;
156
157        setPermissions(permissionValues);
158        setIntentFilterActionsAndCategories(activityIntentActionValues, broadcastIntentActionValues,
159                serviceIntentActionValues, intentCategoryValues);
160        setOptionalLibraries(platformLibraries, optionalLibraries);
161    }
162
163    /**
164     * Returns an {@link IDescriptorProvider} from a given Id.
165     * The Id can be one of {@link #DESCRIPTOR_MANIFEST}, {@link #DESCRIPTOR_LAYOUT},
166     * {@link #DESCRIPTOR_MENU}, or {@link #DESCRIPTOR_OTHER_XML}.
167     * All other values will throw an {@link IllegalArgumentException}.
168     */
169    public IDescriptorProvider getDescriptorProvider(int descriptorId) {
170        switch (descriptorId) {
171            case DESCRIPTOR_MANIFEST:
172                return mManifestDescriptors;
173            case DESCRIPTOR_LAYOUT:
174                return mLayoutDescriptors;
175            case DESCRIPTOR_MENU:
176                return mMenuDescriptors;
177            case DESCRIPTOR_OTHER_XML:
178                return mOtherXmlDescriptors;
179            case DESCRIPTOR_RESOURCES:
180                // FIXME: since it's hard-coded the Resources Descriptors are not platform dependent.
181                return ValuesDescriptors.getInstance();
182            case DESCRIPTOR_PREFERENCES:
183                return mOtherXmlDescriptors.getPreferencesProvider();
184            case DESCRIPTOR_APPWIDGET_PROVIDER:
185                return mOtherXmlDescriptors.getAppWidgetProvider();
186            case DESCRIPTOR_SEARCHABLE:
187                return mOtherXmlDescriptors.getSearchableProvider();
188            case DESCRIPTOR_DRAWABLE:
189                return mDrawableDescriptors;
190            case DESCRIPTOR_ANIMATOR:
191                return mAnimatorDescriptors;
192            case DESCRIPTOR_ANIM:
193                return mAnimDescriptors;
194            case DESCRIPTOR_COLOR:
195                return mColorDescriptors;
196            default :
197                 throw new IllegalArgumentException();
198        }
199    }
200
201    /**
202     * Returns the manifest descriptors.
203     */
204    public AndroidManifestDescriptors getManifestDescriptors() {
205        return mManifestDescriptors;
206    }
207
208    /**
209     * Returns the drawable descriptors
210     */
211    public DrawableDescriptors getDrawableDescriptors() {
212        return mDrawableDescriptors;
213    }
214
215    /**
216     * Returns the animation descriptors
217     */
218    public AnimDescriptors getAnimDescriptors() {
219        return mAnimDescriptors;
220    }
221
222    /**
223     * Returns the color descriptors
224     */
225    public ColorDescriptors getColorDescriptors() {
226        return mColorDescriptors;
227    }
228
229    /**
230     * Returns the animator descriptors
231     */
232    public AnimatorDescriptors getAnimatorDescriptors() {
233        return mAnimatorDescriptors;
234    }
235
236    /**
237     * Returns the layout Descriptors.
238     */
239    public LayoutDescriptors getLayoutDescriptors() {
240        return mLayoutDescriptors;
241    }
242
243    /**
244     * Returns the menu descriptors.
245     */
246    public MenuDescriptors getMenuDescriptors() {
247        return mMenuDescriptors;
248    }
249
250    /**
251     * Returns the XML descriptors
252     */
253    public OtherXmlDescriptors getXmlDescriptors() {
254        return mOtherXmlDescriptors;
255    }
256
257    /**
258     * Returns this list of possible values for an XML attribute.
259     * <p/>This should only be called for attributes for which possible values depend on the
260     * parent element node.
261     * <p/>For attributes that have the same values no matter the parent node, use
262     * {@link #getEnumValueMap()}.
263     * @param elementName the name of the element containing the attribute.
264     * @param attributeName the name of the attribute
265     * @return an array of String with the possible values, or <code>null</code> if no values were
266     * found.
267     */
268    public String[] getAttributeValues(String elementName, String attributeName) {
269        String key = String.format("(%1$s,%2$s)", elementName, attributeName); //$NON-NLS-1$
270        return mAttributeValues.get(key);
271    }
272
273    /**
274     * Returns this list of possible values for an XML attribute.
275     * <p/>This should only be called for attributes for which possible values depend on the
276     * parent and great-grand-parent element node.
277     * <p/>The typical example of this is for the 'name' attribute under
278     * activity/intent-filter/action
279     * <p/>For attributes that have the same values no matter the parent node, use
280     * {@link #getEnumValueMap()}.
281     * @param elementName the name of the element containing the attribute.
282     * @param attributeName the name of the attribute
283     * @param greatGrandParentElementName the great-grand-parent node.
284     * @return an array of String with the possible values, or <code>null</code> if no values were
285     * found.
286     */
287    public String[] getAttributeValues(String elementName, String attributeName,
288            String greatGrandParentElementName) {
289        if (greatGrandParentElementName != null) {
290            String key = String.format("(%1$s,%2$s,%3$s)", //$NON-NLS-1$
291                    greatGrandParentElementName, elementName, attributeName);
292            String[] values = mAttributeValues.get(key);
293            if (values != null) {
294                return values;
295            }
296        }
297
298        return getAttributeValues(elementName, attributeName);
299    }
300
301    /**
302     * Returns the enum values map.
303     * <p/>The map defines the possible values for XML attributes. The key is the attribute name
304     * and the value is a map of (string, integer) in which the key (string) is the name of
305     * the value, and the Integer is the numerical value in the compiled binary XML files.
306     */
307    public Map<String, Map<String, Integer>> getEnumValueMap() {
308        return mEnumValueMap;
309    }
310
311    /**
312     * Returns the {@link ProjectResources} containing the Framework Resources.
313     */
314    public ResourceRepository getFrameworkResources() {
315        return mFrameworkResources;
316    }
317
318    /**
319     * Returns a {@link LayoutLibrary} object possibly containing a {@link LayoutBridge} object.
320     * <p/>If {@link LayoutLibrary#getBridge()} is <code>null</code>,
321     * {@link LayoutBridge#getStatus()} will contain the reason (either {@link LoadStatus#LOADING}
322     * or {@link LoadStatus#FAILED}).
323     * <p/>Valid {@link LayoutBridge} objects are always initialized before being returned.
324     */
325    public synchronized LayoutLibrary getLayoutLibrary() {
326        if (mLayoutBridgeInit == false && mLayoutLibrary.getStatus() == LoadStatus.LOADED) {
327            boolean ok = mLayoutLibrary.init(
328                    mTarget.getProperties(),
329                    new File(mTarget.getPath(IAndroidTarget.FONTS)),
330                    getEnumValueMap(),
331                    new LayoutLog() {
332
333                        @Override
334                        public void error(String tag, String message, Throwable throwable,
335                                Object data) {
336                            AdtPlugin.log(throwable, message);
337                        }
338
339                        @Override
340                        public void error(String tag, String message, Object data) {
341                            AdtPlugin.log(IStatus.ERROR, message);
342                        }
343
344                        @Override
345                        public void warning(String tag, String message, Object data) {
346                            AdtPlugin.log(IStatus.WARNING, message);
347                        }
348                    });
349            if (!ok) {
350                AdtPlugin.log(IStatus.ERROR,
351                        "LayoutLibrary initialization failed");
352            }
353            mLayoutBridgeInit = true;
354        }
355
356        return mLayoutLibrary;
357    }
358
359    /**
360     * Sets the permission values
361     * @param permissionValues the list of permissions
362     */
363    private void setPermissions(String[] permissionValues) {
364        setValues("(uses-permission,android:name)", permissionValues);   //$NON-NLS-1$
365        setValues("(application,android:permission)", permissionValues); //$NON-NLS-1$
366        setValues("(activity,android:permission)", permissionValues);    //$NON-NLS-1$
367        setValues("(receiver,android:permission)", permissionValues);    //$NON-NLS-1$
368        setValues("(service,android:permission)", permissionValues);     //$NON-NLS-1$
369        setValues("(provider,android:permission)", permissionValues);    //$NON-NLS-1$
370    }
371
372    private void setIntentFilterActionsAndCategories(String[] activityIntentActions,
373            String[] broadcastIntentActions, String[] serviceIntentActions,
374            String[] intentCategoryValues) {
375        setValues("(activity,action,android:name)", activityIntentActions);  //$NON-NLS-1$
376        setValues("(receiver,action,android:name)", broadcastIntentActions); //$NON-NLS-1$
377        setValues("(service,action,android:name)", serviceIntentActions);    //$NON-NLS-1$
378        setValues("(category,android:name)", intentCategoryValues);          //$NON-NLS-1$
379    }
380
381    private void setOptionalLibraries(String[] platformLibraries,
382            IOptionalLibrary[] optionalLibraries) {
383
384        ArrayList<String> libs = new ArrayList<String>();
385
386        if (platformLibraries != null) {
387            for (String name : platformLibraries) {
388                libs.add(name);
389            }
390        }
391
392        if (optionalLibraries != null) {
393            for (int i = 0; i < optionalLibraries.length; i++) {
394                libs.add(optionalLibraries[i].getName());
395            }
396        }
397        setValues("(uses-library,android:name)",  libs.toArray(new String[libs.size()]));
398    }
399
400    /**
401     * Sets a (name, values) pair in the hash map.
402     * <p/>
403     * If the name is already present in the map, it is first removed.
404     * @param name the name associated with the values.
405     * @param values The values to add.
406     */
407    private void setValues(String name, String[] values) {
408        mAttributeValues.remove(name);
409        mAttributeValues.put(name, values);
410    }
411
412    public void dispose() {
413        if (mLayoutLibrary != null) {
414            mLayoutLibrary.dispose();
415        }
416    }
417}
418