From cc2cf183f6e73a20b2580db743ca467e8932a841 Mon Sep 17 00:00:00 2001 From: Raphael Moll Date: Wed, 21 Jul 2010 09:42:24 -0400 Subject: [PATCH] ADT GLE2: Let groovy rules access a public API of the RulesEngine. This is done by injecting a property in the groovy rule instance with a new interface, the IClientRulesEngine. The client callback currently has 2 useful methods: debugPrintf (which has been moved out of INode) and loadRule(). This last one is the key here, it allows one rule to request another one to be loaded and get its object. Change-Id: I2881854e33cd3b41565dd1e16aaba1484ef765db --- .../gscripts/BaseView.groovy | 4 ++ .../layout/gscripts/IClientRulesEngine.java | 55 ++++++++++++++++++++ .../eclipse/adt/editors/layout/gscripts/INode.java | 7 --- .../adt/editors/layout/gscripts/IViewRule.java | 10 ++++ .../adt/internal/editors/layout/gre/NodeProxy.java | 14 ++--- .../internal/editors/layout/gre/RulesEngine.java | 60 ++++++++++++++++++++++ 6 files changed, 136 insertions(+), 14 deletions(-) create mode 100755 eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/BaseView.groovy b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/BaseView.groovy index 8a479d0f7..95c2d7320 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/BaseView.groovy +++ b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/BaseView.groovy @@ -42,6 +42,10 @@ public class BaseView implements IViewRule { public boolean onInitialize(String fqcn) { // This base rule can handle any class. mFqcn = fqcn + + // For debugging and as an example of how to use the injected _rules_engine property. + _rules_engine.debugPrintf("Initialize() of %s", _rules_engine.getFqcn()); + return true; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java new file mode 100755 index 000000000..a9ff834a3 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 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.editors.layout.gscripts; + + + +/** + * A Client Rules Engine is a set of methods that {@link IViewRule}s can use to + * access the client public API of the Rules Engine. Rules can access it via + * the property "_rules_engine" which is dynamically added to {@link IViewRule} + * instances on creation. + * + * @see IViewRule#RULES_ENGINE + */ +public interface IClientRulesEngine { + + /** + * Returns the FQCN for which the rule was loaded. + */ + String getFqcn(); + + /** + * Prints a debug line in the Eclipse console using the ADT formatter. + * + * @param msg A String format message. + * @param params Optional parameters for the message. + */ + void debugPrintf(String msg, Object...params); + + /** + * Loads and returns an {@link IViewRule} for the given FQCN. + * + * @param fqcn A non-null, non-empty FQCN for the rule to load. + * @return The rule that best matches the given FQCN according to the + * inheritance chain. Rules are cached and requesting the same FQCN twice + * is fast and will return the same rule instance. + */ + IViewRule loadRule(String fqcn); +} + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INode.java index b850d606f..2d7b7deab 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INode.java @@ -185,13 +185,6 @@ public interface INode { // ----------- - /** TODO: this is a hack. Shouldn't be here but instead part of some kind of helper - * given to IViewRule implementations. - */ - void debugPrintf(String msg, Object...params); - - // ----------- - /** * An XML attribute in an {@link INode}. *

diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java index 6200d8adb..86027e1c9 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java @@ -34,10 +34,20 @@ import java.util.Map; * Rule instances are stateless. They are created once per View class to handle and are shared * across platforms or editor instances. As such, rules methods should never cache editor-specific * arguments that they might receive. + *

+ * When rules are instantiated, a property "_rules_engine" is dynamically added which references + * the {@link IClientRulesEngine} created for this rule. */ public interface IViewRule { /** + * The name of the property that returns a {@link IClientRulesEngine} created for this + * rules. The instance lets rules use some methods from the rules engine, for example + * for accessing other rules. + */ + final static String RULES_ENGINE = "_rules_engine"; + + /** * This method is called by the rule engine when the script is first loaded. * It gives the rule a chance to initialize itself. * diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java index f95e44df9..052e2abf5 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java @@ -80,13 +80,6 @@ public class NodeProxy implements INode { } } - public void debugPrintf(String msg, Object...params) { - AdtPlugin.printToConsole( - mNode == null ? "Groovy" : mNode.getDescriptor().getXmlLocalName() + ".groovy", - String.format(msg, params) - ); - } - public Rect getBounds() { return mBounds; } @@ -413,4 +406,11 @@ public class NodeProxy implements INode { return null; } + private void debugPrintf(String msg, Object...params) { + AdtPlugin.printToConsole( + mNode == null ? "Groovy" : mNode.getDescriptor().getXmlLocalName() + ".groovy", + String.format(msg, params) + ); + } + } 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 index 6dd2a79b9..acfa64445 100755 --- 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 @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gre; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AndroidConstants; import com.android.ide.eclipse.adt.editors.layout.gscripts.DropFeedback; +import com.android.ide.eclipse.adt.editors.layout.gscripts.IClientRulesEngine; import com.android.ide.eclipse.adt.editors.layout.gscripts.IDragElement; import com.android.ide.eclipse.adt.editors.layout.gscripts.IGraphics; import com.android.ide.eclipse.adt.editors.layout.gscripts.INode; @@ -42,8 +43,10 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; +import groovy.lang.ExpandoMetaClass; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyCodeSource; +import groovy.lang.GroovyObject; import groovy.lang.GroovyResourceLoader; import java.io.InputStream; @@ -506,6 +509,10 @@ public class RulesEngine { private IViewRule initializeRule(IViewRule rule, String targetFqcn) { try { + if (rule instanceof GroovyObject) { + initializeMetaClass((GroovyObject) rule, targetFqcn); + } + if (rule.onInitialize(targetFqcn)) { // Add it to the cache and return it mRulesCache.put(targetFqcn, rule); @@ -523,6 +530,31 @@ public class RulesEngine { } /** + * Initializes a custom meta class for the given {@link GroovyObject}. + * This is used to add a meta "_rules_engine" property to the {@link IViewRule} instances. + * + * @param instance The {@link IViewRule} groovy object to modify. + * @param targetFqcn The FQCN for the new {@link IClientRulesEngine}. + */ + private void initializeMetaClass(GroovyObject instance, final String targetFqcn) { + + final ClientRulesEngineImpl mClient = new ClientRulesEngineImpl(targetFqcn); + + ExpandoMetaClass mc = new ExpandoMetaClass(instance.getClass(), false) { + @Override + public Object getProperty(Object object, String name) { + if (IViewRule.RULES_ENGINE.equals(name)) { + return mClient; + } + return super.getProperty(object, name); + } + }; + mc.initialize(); + + instance.setMetaClass(mc); + } + + /** * Actually load a groovy script and instantiate an {@link IViewRule} from it. * On error, outputs (hopefully meaningful) groovy error messages. * @@ -646,4 +678,32 @@ public class RulesEngine { } } + /** + * Implementation of {@link IClientRulesEngine}. This provide {@link IViewRule} clients + * with a few methods they can use to use functionality from this {@link RulesEngine}. + */ + private class ClientRulesEngineImpl implements IClientRulesEngine { + + private final String mFqcn; + + public ClientRulesEngineImpl(String fqcn) { + mFqcn = fqcn; + } + + public String getFqcn() { + return mFqcn; + } + + public void debugPrintf(String msg, Object... params) { + AdtPlugin.printToConsole( + mFqcn == null ? "Groovy" : mFqcn, + String.format(msg, params) + ); + } + + public IViewRule loadRule(String fqcn) { + return RulesEngine.this.loadRule(fqcn, fqcn); + } + } + } -- 2.11.0