diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RuleLoader.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RuleLoader.java | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RuleLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RuleLoader.java new file mode 100644 index 000000000..4f49a7545 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RuleLoader.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.adt.internal.editors.layout.gre; + +import com.android.ide.common.api.IViewRule; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.sdk.ProjectState; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.sdklib.internal.project.ProjectProperties; +import com.android.utils.Pair; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.QualifiedName; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +/** + * The {@link RuleLoader} is responsible for loading (and unloading) + * {@link IViewRule} classes. There is typically one {@link RuleLoader} + * per project. + */ +public class RuleLoader { + /** + * Qualified name for the per-project non-persistent property storing the + * {@link RuleLoader} for this project + */ + private final static QualifiedName RULE_LOADER = new QualifiedName(AdtPlugin.PLUGIN_ID, + "ruleloader"); //$NON-NLS-1$ + + private final IProject mProject; + private ClassLoader mUserClassLoader; + private List<Pair<File, Long>> mUserJarTimeStamps; + private long mLastCheckTimeStamp; + + /** + * Flag set when we've attempted to initialize the {@link #mUserClassLoader} + * already + */ + private boolean mUserClassLoaderInited; + + /** + * Returns the {@link RuleLoader} for the given project + * + * @param project the project the loader is associated with + * @return an {@RuleLoader} for the given project, + * never null + */ + public static RuleLoader get(IProject project) { + RuleLoader loader = null; + try { + loader = (RuleLoader) project.getSessionProperty(RULE_LOADER); + } catch (CoreException e) { + // Not a problem; we will just create a new one + } + if (loader == null) { + loader = new RuleLoader(project); + try { + project.setSessionProperty(RULE_LOADER, loader); + } catch (CoreException e) { + AdtPlugin.log(e, "Can't store RuleLoader"); + } + } + return loader; + } + + /** Do not call; use the {@link #get} factory method instead. */ + private RuleLoader(IProject project) { + mProject = project; + } + + /** + * Find out whether the given project has 3rd party ViewRules, and if so + * return a ClassLoader which can locate them. If not, return null. + * @param project The project to load user rules from + * @return A class loader which can user view rules, or otherwise null + */ + private ClassLoader computeUserClassLoader(IProject project) { + // Default place to locate layout rules. The user may also add to this + // path by defining a config property specifying + // additional .jar files to search via a the layoutrules.jars property. + ProjectState state = Sdk.getProjectState(project); + ProjectProperties projectProperties = state.getProperties(); + + // Ensure we have the latest & greatest version of the properties. + // This allows users to reopen editors in a running Eclipse instance + // to get updated view rule jars + projectProperties.reload(); + + String path = projectProperties.getProperty( + ProjectProperties.PROPERTY_RULES_PATH); + + if (path != null && path.length() > 0) { + + mUserJarTimeStamps = new ArrayList<Pair<File, Long>>(); + mLastCheckTimeStamp = System.currentTimeMillis(); + + List<URL> urls = new ArrayList<URL>(); + String[] pathElements = path.split(File.pathSeparator); + for (String pathElement : pathElements) { + pathElement = pathElement.trim(); // Avoid problems with trailing whitespace etc + File pathFile = new File(pathElement); + if (!pathFile.isAbsolute()) { + pathFile = new File(project.getLocation().toFile(), pathElement); + } + // Directories and jar files are okay. Do we need to + // validate the files here as .jar files? + if (pathFile.isFile() || pathFile.isDirectory()) { + URL url; + try { + url = pathFile.toURI().toURL(); + urls.add(url); + + mUserJarTimeStamps.add(Pair.of(pathFile, pathFile.lastModified())); + } catch (MalformedURLException e) { + AdtPlugin.log(IStatus.WARNING, + "Invalid URL: %1$s", //$NON-NLS-1$ + e.toString()); + } + } + } + + if (urls.size() > 0) { + return new URLClassLoader(urls.toArray(new URL[urls.size()]), + RulesEngine.class.getClassLoader()); + } + } + + return null; + } + + /** + * Return the class loader to use for custom views, or null if no custom + * view rules are registered for the project. Note that this class loader + * can change over time (if the jar files are updated), so callers should be + * prepared to unload previous instances. + * + * @return a class loader to use for custom view rules, or null + */ + public ClassLoader getClassLoader() { + if (mUserClassLoader == null) { + // Only attempt to load rule paths once. + // TODO: Check the timestamp on the project.properties file so we can dynamically + // pick up cases where the user edits the path + if (!mUserClassLoaderInited) { + mUserClassLoaderInited = true; + mUserClassLoader = computeUserClassLoader(mProject); + } + } else { + // Check the timestamp on the jar files in the custom view path to see if we + // need to reload the classes (but only do this at most every 3 seconds) + if (mUserJarTimeStamps != null) { + long time = System.currentTimeMillis(); + if (time - mLastCheckTimeStamp > 3000) { + mLastCheckTimeStamp = time; + for (Pair<File, Long> pair : mUserJarTimeStamps) { + File file = pair.getFirst(); + Long prevModified = pair.getSecond(); + long modified = file.lastModified(); + if (prevModified.longValue() != modified) { + mUserClassLoaderInited = true; + mUserJarTimeStamps = null; + mUserClassLoader = computeUserClassLoader(mProject); + } + } + } + } + } + + return mUserClassLoader; + } +} |