diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java | 876 |
1 files changed, 0 insertions, 876 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java deleted file mode 100644 index 8f9923749..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java +++ /dev/null @@ -1,876 +0,0 @@ -/* - * Copyright (C) 2009 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 static com.android.SdkConstants.ANDROID_WIDGET_PREFIX; -import static com.android.SdkConstants.VIEW_MERGE; -import static com.android.SdkConstants.VIEW_TAG; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.api.DropFeedback; -import com.android.ide.common.api.IDragElement; -import com.android.ide.common.api.IGraphics; -import com.android.ide.common.api.INode; -import com.android.ide.common.api.IViewRule; -import com.android.ide.common.api.InsertType; -import com.android.ide.common.api.Point; -import com.android.ide.common.api.Rect; -import com.android.ide.common.api.RuleAction; -import com.android.ide.common.api.SegmentType; -import com.android.ide.common.layout.ViewRule; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GCWrapper; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement; -import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.sdklib.IAndroidTarget; - -import org.eclipse.core.resources.IProject; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -/** - * The rule engine manages the layout rules and interacts with them. - * There's one {@link RulesEngine} instance per layout editor. - * Each instance has 2 sets of rules: the static ADT rules (shared across all instances) - * and the project specific rules (local to the current instance / layout editor). - */ -public class RulesEngine { - private final IProject mProject; - private final Map<Object, IViewRule> mRulesCache = new HashMap<Object, IViewRule>(); - - /** - * The type of any upcoming node manipulations performed by the {@link IViewRule}s. - * When actions are performed in the tool (like a paste action, or a drag from palette, - * or a drag move within the canvas, etc), these are different types of inserts, - * and we don't want to have the rules track them closely (and pass them back to us - * in the {@link INode#insertChildAt} methods etc), so instead we track the state - * here on behalf of the currently executing rule. - */ - private InsertType mInsertType = InsertType.CREATE; - - /** - * Per-project loader for custom view rules - */ - private RuleLoader mRuleLoader; - private ClassLoader mUserClassLoader; - - /** - * The editor which owns this {@link RulesEngine} - */ - private final GraphicalEditorPart mEditor; - - /** - * Creates a new {@link RulesEngine} associated with the selected project. - * <p/> - * The rules engine will look in the project for a tools jar to load custom view rules. - * - * @param editor the editor which owns this {@link RulesEngine} - * @param project A non-null open project. - */ - public RulesEngine(GraphicalEditorPart editor, IProject project) { - mProject = project; - mEditor = editor; - - mRuleLoader = RuleLoader.get(project); - } - - /** - * Returns the {@link IProject} on which the {@link RulesEngine} was created. - */ - public IProject getProject() { - return mProject; - } - - /** - * Returns the {@link GraphicalEditorPart} for which the {@link RulesEngine} was - * created. - * - * @return the associated editor - */ - public GraphicalEditorPart getEditor() { - return mEditor; - } - - /** - * Called by the owner of the {@link RulesEngine} when it is going to be disposed. - * This frees some resources, such as the project's folder monitor. - */ - public void dispose() { - clearCache(); - } - - /** - * Invokes {@link IViewRule#getDisplayName()} on the rule matching the specified element. - * - * @param element The view element to target. Can be null. - * @return Null if the rule failed, there's no rule or the rule does not want to override - * the display name. Otherwise, a string as returned by the rule. - */ - public String callGetDisplayName(UiViewElementNode element) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(element); - - if (rule != null) { - try { - return rule.getDisplayName(); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.getDisplayName() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - /** - * Invokes {@link IViewRule#addContextMenuActions(List, INode)} on the rule matching the specified element. - * - * @param selectedNode The node selected. Never null. - * @return Null if the rule failed, there's no rule or the rule does not provide - * any custom menu actions. Otherwise, a list of {@link RuleAction}. - */ - @Nullable - public List<RuleAction> callGetContextMenu(NodeProxy selectedNode) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(selectedNode.getNode()); - - if (rule != null) { - try { - mInsertType = InsertType.CREATE; - List<RuleAction> actions = new ArrayList<RuleAction>(); - rule.addContextMenuActions(actions, selectedNode); - Collections.sort(actions); - - return actions; - } catch (Exception e) { - AdtPlugin.log(e, "%s.getContextMenu() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - /** - * Calls the selected node to return its default action - * - * @param selectedNode the node to apply the action to - * @return the default action id - */ - public String callGetDefaultActionId(@NonNull NodeProxy selectedNode) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(selectedNode.getNode()); - - if (rule != null) { - try { - mInsertType = InsertType.CREATE; - return rule.getDefaultActionId(selectedNode); - } catch (Exception e) { - AdtPlugin.log(e, "%s.getDefaultAction() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - /** - * Invokes {@link IViewRule#addLayoutActions(List, INode, List)} on the rule - * matching the specified element. - * - * @param actions The list of actions to add layout actions into - * @param parentNode The layout node - * @param children The selected children of the node, if any (used to - * initialize values of child layout controls, if applicable) - * @return Null if the rule failed, there's no rule or the rule does not - * provide any custom menu actions. Otherwise, a list of - * {@link RuleAction}. - */ - public List<RuleAction> callAddLayoutActions(List<RuleAction> actions, - NodeProxy parentNode, List<NodeProxy> children ) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(parentNode.getNode()); - - if (rule != null) { - try { - mInsertType = InsertType.CREATE; - rule.addLayoutActions(actions, parentNode, children); - } catch (Exception e) { - AdtPlugin.log(e, "%s.getContextMenu() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - /** - * Invokes {@link IViewRule#getSelectionHint(INode, INode)} - * on the rule matching the specified element. - * - * @param parentNode The parent of the node selected. Never null. - * @param childNode The child node that was selected. Never null. - * @return a list of strings to be displayed, or null or empty to display nothing - */ - public List<String> callGetSelectionHint(NodeProxy parentNode, NodeProxy childNode) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(parentNode.getNode()); - - if (rule != null) { - try { - return rule.getSelectionHint(parentNode, childNode); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.getSelectionHint() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - public void callPaintSelectionFeedback(GCWrapper gcWrapper, NodeProxy parentNode, - List<? extends INode> childNodes, Object view) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(parentNode.getNode()); - - if (rule != null) { - try { - rule.paintSelectionFeedback(gcWrapper, parentNode, childNodes, view); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.callPaintSelectionFeedback() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - } - - /** - * Called when the d'n'd starts dragging over the target node. - * If interested, returns a DropFeedback passed to onDrop/Move/Leave/Paint. - * If not interested in drop, return false. - * Followed by a paint. - */ - public DropFeedback callOnDropEnter(NodeProxy targetNode, - Object targetView, IDragElement[] elements) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(targetNode.getNode()); - - if (rule != null) { - try { - return rule.onDropEnter(targetNode, targetView, elements); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.onDropEnter() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - /** - * Called after onDropEnter. - * Returns a DropFeedback passed to onDrop/Move/Leave/Paint (typically same - * as input one). - */ - public DropFeedback callOnDropMove(NodeProxy targetNode, - IDragElement[] elements, - DropFeedback feedback, - Point where) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(targetNode.getNode()); - - if (rule != null) { - try { - return rule.onDropMove(targetNode, elements, feedback, where); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.onDropMove() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - /** - * Called when drop leaves the target without actually dropping - */ - public void callOnDropLeave(NodeProxy targetNode, - IDragElement[] elements, - DropFeedback feedback) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(targetNode.getNode()); - - if (rule != null) { - try { - rule.onDropLeave(targetNode, elements, feedback); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.onDropLeave() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - } - - /** - * Called when drop is released over the target to perform the actual drop. - */ - public void callOnDropped(NodeProxy targetNode, - IDragElement[] elements, - DropFeedback feedback, - Point where, - InsertType insertType) { - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(targetNode.getNode()); - - if (rule != null) { - try { - mInsertType = insertType; - rule.onDropped(targetNode, elements, feedback, where); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.onDropped() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - } - - /** - * Called when a paint has been requested via DropFeedback. - */ - public void callDropFeedbackPaint(IGraphics gc, - NodeProxy targetNode, - DropFeedback feedback) { - if (gc != null && feedback != null && feedback.painter != null) { - try { - feedback.painter.paint(gc, targetNode, feedback); - } catch (Exception e) { - AdtPlugin.log(e, "DropFeedback.painter failed: %s", - e.toString()); - } - } - } - - /** - * Called when pasting elements in an existing document on the selected target. - * - * @param targetNode The first node selected. - * @param targetView The view object for the target node, or null if not known - * @param pastedElements The elements being pasted. - * @return the parent node the paste was applied into - */ - public NodeProxy callOnPaste(NodeProxy targetNode, Object targetView, - SimpleElement[] pastedElements) { - - // Find a target which accepts children. If you for example select a button - // and attempt to paste, this will reselect the parent of the button as the paste - // target. (This is a loop rather than just checking the direct parent since - // we will soon ask each child whether they are *willing* to accept the new child. - // A ScrollView for example, which only accepts one child, might also say no - // and delegate to its parent in turn. - INode parent = targetNode; - while (parent instanceof NodeProxy) { - NodeProxy np = (NodeProxy) parent; - if (np.getNode() != null && np.getNode().getDescriptor() != null) { - ElementDescriptor descriptor = np.getNode().getDescriptor(); - if (descriptor.hasChildren()) { - targetNode = np; - break; - } - } - parent = parent.getParent(); - } - - // try to find a rule for this element's FQCN - IViewRule rule = loadRule(targetNode.getNode()); - - if (rule != null) { - try { - mInsertType = InsertType.PASTE; - rule.onPaste(targetNode, targetView, pastedElements); - - } catch (Exception e) { - AdtPlugin.log(e, "%s.onPaste() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - - return targetNode; - } - - // ---- Resize operations ---- - - public DropFeedback callOnResizeBegin(NodeProxy child, NodeProxy parent, Rect newBounds, - SegmentType horizontalEdge, SegmentType verticalEdge, Object childView, - Object parentView) { - IViewRule rule = loadRule(parent.getNode()); - - if (rule != null) { - try { - return rule.onResizeBegin(child, parent, horizontalEdge, verticalEdge, - childView, parentView); - } catch (Exception e) { - AdtPlugin.log(e, "%s.onResizeBegin() failed: %s", rule.getClass().getSimpleName(), - e.toString()); - } - } - - return null; - } - - public void callOnResizeUpdate(DropFeedback feedback, NodeProxy child, NodeProxy parent, - Rect newBounds, int modifierMask) { - IViewRule rule = loadRule(parent.getNode()); - - if (rule != null) { - try { - rule.onResizeUpdate(feedback, child, parent, newBounds, modifierMask); - } catch (Exception e) { - AdtPlugin.log(e, "%s.onResizeUpdate() failed: %s", rule.getClass().getSimpleName(), - e.toString()); - } - } - } - - public void callOnResizeEnd(DropFeedback feedback, NodeProxy child, NodeProxy parent, - Rect newBounds) { - IViewRule rule = loadRule(parent.getNode()); - - if (rule != null) { - try { - rule.onResizeEnd(feedback, child, parent, newBounds); - } catch (Exception e) { - AdtPlugin.log(e, "%s.onResizeEnd() failed: %s", rule.getClass().getSimpleName(), - e.toString()); - } - } - } - - // ---- Creation customizations ---- - - /** - * Invokes the create hooks ({@link IViewRule#onCreate}, - * {@link IViewRule#onChildInserted} when a new child has been created/pasted/moved, and - * is inserted into a given parent. The parent may be null (for example when rendering - * top level items for preview). - * - * @param editor the XML editor to apply edits to the model for (performed by view - * rules) - * @param parentNode the parent XML node, or null if unknown - * @param childNode the XML node of the new node, never null - * @param overrideInsertType If not null, specifies an explicit insert type to use for - * edits made during the customization - */ - public void callCreateHooks( - AndroidXmlEditor editor, - NodeProxy parentNode, NodeProxy childNode, - InsertType overrideInsertType) { - IViewRule parentRule = null; - - if (parentNode != null) { - UiViewElementNode parentUiNode = parentNode.getNode(); - parentRule = loadRule(parentUiNode); - } - - if (overrideInsertType != null) { - mInsertType = overrideInsertType; - } - - UiViewElementNode newUiNode = childNode.getNode(); - IViewRule childRule = loadRule(newUiNode); - if (childRule != null || parentRule != null) { - callCreateHooks(editor, mInsertType, parentRule, parentNode, - childRule, childNode); - } - } - - private static void callCreateHooks( - final AndroidXmlEditor editor, final InsertType insertType, - final IViewRule parentRule, final INode parentNode, - final IViewRule childRule, final INode newNode) { - // Notify the parent about the new child in case it wants to customize it - // (For example, a ScrollView parent can go and set all its children's layout params to - // fill the parent.) - if (!editor.isEditXmlModelPending()) { - editor.wrapEditXmlModel(new Runnable() { - @Override - public void run() { - callCreateHooks(editor, insertType, - parentRule, parentNode, childRule, newNode); - } - }); - return; - } - - if (parentRule != null) { - parentRule.onChildInserted(newNode, parentNode, insertType); - } - - // Look up corresponding IViewRule, and notify the rule about - // this create action in case it wants to customize the new object. - // (For example, a rule for TabHosts can go and create a default child tab - // when you create it.) - if (childRule != null) { - childRule.onCreate(newNode, parentNode, insertType); - } - - if (parentNode != null) { - ((NodeProxy) parentNode).applyPendingChanges(); - } - } - - /** - * Set the type of insert currently in progress - * - * @param insertType the insert type to use for the next operation - */ - public void setInsertType(InsertType insertType) { - mInsertType = insertType; - } - - /** - * Return the type of insert currently in progress - * - * @return the type of insert currently in progress - */ - public InsertType getInsertType() { - return mInsertType; - } - - // ---- Deletion ---- - - public void callOnRemovingChildren(NodeProxy parentNode, - List<INode> children) { - if (parentNode != null) { - UiViewElementNode parentUiNode = parentNode.getNode(); - IViewRule parentRule = loadRule(parentUiNode); - if (parentRule != null) { - try { - parentRule.onRemovingChildren(children, parentNode, - mInsertType == InsertType.MOVE_WITHIN); - } catch (Exception e) { - AdtPlugin.log(e, "%s.onDispose() failed: %s", - parentRule.getClass().getSimpleName(), - e.toString()); - } - } - } - } - - // ---- private --- - - /** - * Returns the descriptor for the base View class. - * This could be null if the SDK or the given platform target hasn't loaded yet. - */ - private ViewElementDescriptor getBaseViewDescriptor() { - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - IAndroidTarget target = currentSdk.getTarget(mProject); - if (target != null) { - AndroidTargetData data = currentSdk.getTargetData(target); - return data.getLayoutDescriptors().getBaseViewDescriptor(); - } - } - return null; - } - - /** - * Clear the Rules cache. Calls onDispose() on each rule. - */ - private void clearCache() { - // The cache can contain multiple times the same rule instance for different - // keys (e.g. the UiViewElementNode key vs. the FQCN string key.) So transfer - // all values to a unique set. - HashSet<IViewRule> rules = new HashSet<IViewRule>(mRulesCache.values()); - - mRulesCache.clear(); - - for (IViewRule rule : rules) { - if (rule != null) { - try { - rule.onDispose(); - } catch (Exception e) { - AdtPlugin.log(e, "%s.onDispose() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - } - } - } - - /** - * Checks whether the project class loader has changed, and if so - * unregisters any view rules that use classes from the old class loader. It - * then returns the class loader to be used. - */ - private ClassLoader updateClassLoader() { - ClassLoader classLoader = mRuleLoader.getClassLoader(); - if (mUserClassLoader != null && classLoader != mUserClassLoader) { - // We have to unload all the IViewRules from the old class - List<Object> dispose = new ArrayList<Object>(); - for (Map.Entry<Object, IViewRule> entry : mRulesCache.entrySet()) { - IViewRule rule = entry.getValue(); - if (rule.getClass().getClassLoader() == mUserClassLoader) { - dispose.add(entry.getKey()); - } - } - for (Object object : dispose) { - mRulesCache.remove(object); - } - } - - mUserClassLoader = classLoader; - return mUserClassLoader; - } - - /** - * Load a rule using its descriptor. This will try to first load the rule using its - * actual FQCN and if that fails will find the first parent that works in the view - * hierarchy. - */ - private IViewRule loadRule(UiViewElementNode element) { - if (element == null) { - return null; - } - - String targetFqcn = null; - ViewElementDescriptor targetDesc = null; - - ElementDescriptor d = element.getDescriptor(); - if (d instanceof ViewElementDescriptor) { - targetDesc = (ViewElementDescriptor) d; - } - if (d == null || !(d instanceof ViewElementDescriptor)) { - // This should not happen. All views should have some kind of *view* element - // descriptor. Maybe the project is not complete and doesn't build or something. - // In this case, we'll use the descriptor of the base android View class. - targetDesc = getBaseViewDescriptor(); - } - - // Check whether any of the custom view .jar files have changed and if so - // unregister previously cached view rules to force a new view rule to be loaded. - updateClassLoader(); - - // Return the rule if we find it in the cache, even if it was stored as null - // (which means we didn't find it earlier, so don't look for it again) - IViewRule rule = mRulesCache.get(targetDesc); - if (rule != null || mRulesCache.containsKey(targetDesc)) { - return rule; - } - - // Get the descriptor and loop through the super class hierarchy - for (ViewElementDescriptor desc = targetDesc; - desc != null; - desc = desc.getSuperClassDesc()) { - - // Get the FQCN of this View - String fqcn = desc.getFullClassName(); - if (fqcn == null) { - // Shouldn't be happening. - return null; - } - - // The first time we keep the FQCN around as it's the target class we were - // initially trying to load. After, as we move through the hierarchy, the - // target FQCN remains constant. - if (targetFqcn == null) { - targetFqcn = fqcn; - } - - if (fqcn.indexOf('.') == -1) { - // Deal with unknown descriptors; these lack the full qualified path and - // elements in the layout without a package are taken to be in the - // android.widget package. - fqcn = ANDROID_WIDGET_PREFIX + fqcn; - } - - // Try to find a rule matching the "real" FQCN. If we find it, we're done. - // If not, the for loop will move to the parent descriptor. - rule = loadRule(fqcn, targetFqcn); - if (rule != null) { - // We found one. - // As a side effect, loadRule() also cached the rule using the target FQCN. - return rule; - } - } - - // Memorize in the cache that we couldn't find a rule for this descriptor - mRulesCache.put(targetDesc, null); - return null; - } - - /** - * Try to load a rule given a specific FQCN. This looks for an exact match in either - * the ADT scripts or the project scripts and does not look at parent hierarchy. - * <p/> - * Once a rule is found (or not), it is stored in a cache using its target FQCN - * so we don't try to reload it. - * <p/> - * The real FQCN is the actual rule class we're loading, e.g. "android.view.View" - * where target FQCN is the class we were initially looking for, which might be the same as - * the real FQCN or might be a derived class, e.g. "android.widget.TextView". - * - * @param realFqcn The FQCN of the rule class actually being loaded. - * @param targetFqcn The FQCN of the class actually processed, which might be different from - * the FQCN of the rule being loaded. - */ - IViewRule loadRule(String realFqcn, String targetFqcn) { - if (realFqcn == null || targetFqcn == null) { - return null; - } - - // Return the rule if we find it in the cache, even if it was stored as null - // (which means we didn't find it earlier, so don't look for it again) - IViewRule rule = mRulesCache.get(realFqcn); - if (rule != null || mRulesCache.containsKey(realFqcn)) { - return rule; - } - - // Look for class via reflection - try { - // For now, we package view rules for the builtin Android views and - // widgets with the tool in a special package, so look there rather - // than in the same package as the widgets. - String ruleClassName; - ClassLoader classLoader; - if (realFqcn.startsWith("android.") || //$NON-NLS-1$ - realFqcn.equals(VIEW_MERGE) || - realFqcn.endsWith(".GridLayout") || //$NON-NLS-1$ // Temporary special case - // FIXME: Remove this special case as soon as we pull - // the MapViewRule out of this code base and bundle it - // with the add ons - realFqcn.startsWith("com.google.android.maps.")) { //$NON-NLS-1$ - // This doesn't handle a case where there are name conflicts - // (e.g. where there are multiple different views with the same - // class name and only differing in package names, but that's a - // really bad practice in the first place, and if that situation - // should come up in the API we can enhance this algorithm. - String packageName = ViewRule.class.getName(); - packageName = packageName.substring(0, packageName.lastIndexOf('.')); - classLoader = RulesEngine.class.getClassLoader(); - int dotIndex = realFqcn.lastIndexOf('.'); - String baseName = realFqcn.substring(dotIndex+1); - // Capitalize rule class name to match naming conventions, if necessary (<merge>) - if (Character.isLowerCase(baseName.charAt(0))) { - if (baseName.equals(VIEW_TAG)) { - // Hack: ViewRule is generic for the "View" class, so we can't use it - // for the special XML "view" tag (lowercase); instead, the rule is - // named "ViewTagRule" instead. - baseName = "ViewTag"; //$NON-NLS-1$ - } - baseName = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); - } - ruleClassName = packageName + "." + //$NON-NLS-1$ - baseName + "Rule"; //$NON-NLS-1$ - } else { - // Initialize the user-classpath for 3rd party IViewRules, if necessary - classLoader = updateClassLoader(); - if (classLoader == null) { - // The mUserClassLoader can be null; this is the typical scenario, - // when the user is only using builtin layout rules. - // This means however we can't resolve this fqcn since it's not - // in the name space of the builtin rules. - mRulesCache.put(realFqcn, null); - return null; - } - - // For other (3rd party) widgets, look in the same package (though most - // likely not in the same jar!) - ruleClassName = realFqcn + "Rule"; //$NON-NLS-1$ - } - - Class<?> clz = Class.forName(ruleClassName, true, classLoader); - rule = (IViewRule) clz.newInstance(); - return initializeRule(rule, targetFqcn); - } catch (ClassNotFoundException ex) { - // Not an unexpected error - this means that there isn't a helper for this - // class. - } catch (InstantiationException e) { - // This is NOT an expected error: fail. - AdtPlugin.log(e, "load rule error (%s): %s", realFqcn, e.toString()); - } catch (IllegalAccessException e) { - // This is NOT an expected error: fail. - AdtPlugin.log(e, "load rule error (%s): %s", realFqcn, e.toString()); - } - - // Memorize in the cache that we couldn't find a rule for this real FQCN - mRulesCache.put(realFqcn, null); - return null; - } - - /** - * Initialize a rule we just loaded. The rule has a chance to examine the target FQCN - * and bail out. - * <p/> - * Contract: the rule is not in the {@link #mRulesCache} yet and this method will - * cache it using the target FQCN if the rule is accepted. - * <p/> - * The real FQCN is the actual rule class we're loading, e.g. "android.view.View" - * where target FQCN is the class we were initially looking for, which might be the same as - * the real FQCN or might be a derived class, e.g. "android.widget.TextView". - * - * @param rule A rule freshly loaded. - * @param targetFqcn The FQCN of the class actually processed, which might be different from - * the FQCN of the rule being loaded. - * @return The rule if accepted, or null if the rule can't handle that FQCN. - */ - private IViewRule initializeRule(IViewRule rule, String targetFqcn) { - - try { - if (rule.onInitialize(targetFqcn, new ClientRulesEngine(this, targetFqcn))) { - // Add it to the cache and return it - mRulesCache.put(targetFqcn, rule); - return rule; - } else { - rule.onDispose(); - } - } catch (Exception e) { - AdtPlugin.log(e, "%s.onInit() failed: %s", - rule.getClass().getSimpleName(), - e.toString()); - } - - return null; - } -} |