FilterFactory.java revision 227b47625d7482b5b47ad0e4c70ce0a246236ade
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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
18package androidx.media.filterfw;
19
20import android.util.Log;
21
22import dalvik.system.PathClassLoader;
23
24import java.lang.reflect.Constructor;
25import java.util.HashSet;
26
27public class FilterFactory {
28
29    private static FilterFactory mSharedFactory;
30    private HashSet<String> mPackages = new HashSet<String>();
31
32    private static ClassLoader mCurrentClassLoader;
33    private static HashSet<String> mLibraries;
34    private static Object mClassLoaderGuard;
35
36    static {
37        mCurrentClassLoader = Thread.currentThread().getContextClassLoader();
38        mLibraries = new HashSet<String>();
39        mClassLoaderGuard = new Object();
40    }
41
42    private static final String TAG = "FilterFactory";
43    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
44
45    public static FilterFactory sharedFactory() {
46        if (mSharedFactory == null) {
47            mSharedFactory = new FilterFactory();
48        }
49        return mSharedFactory;
50    }
51
52    /**
53     * Adds a new Java library to the list to be scanned for filters.
54     * libraryPath must be an absolute path of the jar file.  This needs to be
55     * static because only one classloader per process can open a shared native
56     * library, which a filter may well have.
57     */
58    public static void addFilterLibrary(String libraryPath) {
59        if (mLogVerbose) Log.v(TAG, "Adding filter library " + libraryPath);
60        synchronized(mClassLoaderGuard) {
61            if (mLibraries.contains(libraryPath)) {
62                if (mLogVerbose) Log.v(TAG, "Library already added");
63                return;
64            }
65            mLibraries.add(libraryPath);
66            // Chain another path loader to the current chain
67            mCurrentClassLoader = new PathClassLoader(libraryPath, mCurrentClassLoader);
68        }
69    }
70
71    public void addPackage(String packageName) {
72        if (mLogVerbose) Log.v(TAG, "Adding package " + packageName);
73        /* TODO: This should use a getPackage call in the caller's context, but no such method
74                 exists.
75        Package pkg = Package.getPackage(packageName);
76        if (pkg == null) {
77            throw new IllegalArgumentException("Unknown filter package '" + packageName + "'!");
78        }
79        */
80        mPackages.add(packageName);
81    }
82
83    public boolean isFilterAvailable(String className) {
84        return getFilterClass(className) != null;
85    }
86
87    public Filter createFilterByClassName(String className, String filterName, MffContext context) {
88        if (mLogVerbose) Log.v(TAG, "Looking up class " + className);
89        Class<? extends Filter> filterClass = getFilterClass(className);
90        if (filterClass == null) {
91            throw new IllegalArgumentException("Unknown filter class '" + className + "'!");
92        }
93        return createFilterByClass(filterClass, filterName, context);
94    }
95
96    public Filter createFilterByClass(Class<? extends Filter> filterClass,
97            String filterName, MffContext context) {
98        // Look for the correct constructor
99        Constructor<? extends Filter> filterConstructor = null;
100        try {
101            filterConstructor = filterClass.getConstructor(MffContext.class, String.class);
102        } catch (NoSuchMethodException e) {
103            throw new IllegalArgumentException("The filter class '" + filterClass
104                + "' does not have a constructor of the form <init>(MffContext, String)!");
105        }
106
107        // Construct the filter
108        Filter filter = null;
109        try {
110            filter = filterConstructor.newInstance(context, filterName);
111        } catch (Throwable t) {
112            throw new RuntimeException("Error creating filter " + filterName + "!", t);
113        }
114
115        if (filter == null) {
116            throw new IllegalArgumentException("Could not construct the filter '"
117                + filterName + "'!");
118        }
119        return filter;
120    }
121
122    private Class<? extends Filter> getFilterClass(String name) {
123        Class<?> filterClass = null;
124
125        // Look for the class in the imported packages
126        for (String packageName : mPackages) {
127            try {
128                if (mLogVerbose) Log.v(TAG, "Trying "+ packageName + "." + name);
129                synchronized(mClassLoaderGuard) {
130                    filterClass = mCurrentClassLoader.loadClass(packageName + "." + name);
131                }
132            } catch (ClassNotFoundException e) {
133                continue;
134            }
135            // Exit loop if class was found.
136            if (filterClass != null) {
137                break;
138            }
139        }
140        Class<? extends Filter> result = null;
141        try {
142            if (filterClass != null) {
143                result = filterClass.asSubclass(Filter.class);
144            }
145        } catch (ClassCastException e) {
146            // Leave result == null
147        }
148        return result;
149    }
150}
151