aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
diff options
context:
space:
mode:
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.java876
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;
- }
-}