Hi there, enclosed you'll find a diff ("svn diff" was run off the "org.apache.bsf" directory), which contains the following changes:
Regards, ---rony |
Index: util/event/generator/EventAdapterGenerator.java =================================================================== --- util/event/generator/EventAdapterGenerator.java (revision 381339) +++ util/event/generator/EventAdapterGenerator.java (working copy) @@ -53,13 +53,19 @@ * please see <http://www.apache.org/>. */ + /* changes: + 2006-02-03, Rony G. Flatscher: added OpenOffice.org support (versions 1.1.x and 2.0.1) + which need special handling due to their omission to tagt heir event + listeners as implementing "java.util.EventListener" inhibiting standard + introspection to identify events; therefore a "manual" branch got introduced + to identify OpenOffice.org event listeners + */ + package org.apache.bsf.util.event.generator; import java.io.*; @@ -69,6 +75,7 @@ { public static AdapterClassLoader ldr = new AdapterClassLoader(); static Class EVENTLISTENER = null; + static Class OPENOFFICE_XEVENTLISTENER = null; static String CLASSPACKAGE = "org/apache/bsf/util/event/adapters/"; static String WRITEDIRECTORY = null; @@ -120,6 +122,18 @@ ex.printStackTrace(); } + + // try to load the OpenOffice.org (OOo) counterpart of EventListener; unfortunately as of 2006 + // OOo's XEventListener does not report to have 'java.util.EventListener' implemented, hence + // Introspector cannot identify events ! + try + { + OPENOFFICE_XEVENTLISTENER = Thread.currentThread().getContextClassLoader().loadClass ("com.sun.star.lang.XEventListener"); + } + catch (Exception e) + { + } + // start of the Java Class File CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xCA); // magic CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xFE); // magic @@ -211,9 +225,13 @@ /* methods that take an EventListener Class Type to create an EventAdapterClass */ public static Class makeEventAdapterClass(Class listenerType,boolean writeClassFile) { - logger.info("EventAdapterGenerator"); + DebugLog.stdoutPrintln("EventAdapterGenerator", DebugLog.BSF_LOG_L3); - if( EVENTLISTENER.isAssignableFrom(listenerType) ) + if( EVENTLISTENER.isAssignableFrom(listenerType) || + // test explicitly OpenOffice.org listener types; as of 2006-02-03 neither 1.1.5 nor + // OOo 2.0.1 do indicate that they implement 'java.lang.EventListener' + ( OPENOFFICE_XEVENTLISTENER!=null && OPENOFFICE_XEVENTLISTENER.isAssignableFrom(listenerType) ) + ) { boolean exceptionable = false; boolean nonExceptionable = false; @@ -560,7 +580,8 @@ { try { - FileOutputStream fos = new FileOutputStream(WRITEDIRECTORY+finalAdapterClassName+".class"); + // removed "WRITEDIRECTORY+", as this path is already part of 'finalAdapterClassName' + FileOutputStream fos = new FileOutputStream(finalAdapterClassName+".class"); fos.write(newClass); fos.close(); } Index: util/event/generator/AdapterClassLoader.java =================================================================== --- util/event/generator/AdapterClassLoader.java (revision 381339) +++ util/event/generator/AdapterClassLoader.java (working copy) @@ -53,18 +53,20 @@ * please see <http://www.apache.org/>. */ + /* changes: + 2006-02-03, Rony G. Flatscher: fixed bug (ClassLoader.loadClass(...) needs "/" and not "." as + path separators + */ + package org.apache.bsf.util.event.generator; import java.util.*; @@ -74,13 +76,14 @@ { if ((c = getLoadedClass(name)) == null) { - c = defineClass(name, b, 0, b.length); + c = defineClass(name.replace('/','.'), b, 0, b.length); // rgf, 2006-02-03 put(name, c); } else Index: util/event/EventAdapterRegistry.java =================================================================== --- util/event/EventAdapterRegistry.java (revision 381339) +++ util/event/EventAdapterRegistry.java (working copy) @@ -99,12 +99,11 @@ // adapterClass = (cl != null) ? cl.loadClass (cn) : Class.forName (cn); adapterClass = (cl != null) ? cl.loadClass (cn) : Thread.currentThread().getContextClassLoader().loadClass (cn); // rgf, 2006-01-05 - } catch (ClassNotFoundException e) { if (dynamic) { // Unable to resolve one, try to generate one. - adapterClass = - EventAdapterGenerator.makeEventAdapterClass (listenerType, false); + adapterClass = // if second argument is set to 'true', then the class file will be stored in the filesystem + EventAdapterGenerator.makeEventAdapterClass (listenerType, false); } } Index: util/BSFEventProcessorReturningEventInfos.java =================================================================== --- util/BSFEventProcessorReturningEventInfos.java (revision 0) +++ util/BSFEventProcessorReturningEventInfos.java (revision 0) @@ -0,0 +1,146 @@ +/* + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * Sanjiva Weerawarana and others at International Business Machines + * Corporation. For more information on the Apache Software Foundation, + * please see <http://www.apache.org/>. + */ + +package org.apache.bsf.util; + +import org.apache.bsf.util.event.*; +import org.apache.bsf.*; +import java.io.PrintStream; +import java.util.Vector; + +/** + * This is used to support binding scripts to be run when an event + * occurs, forwarding the arguments supplied to the event listener. It is an adapted version of + * [EMAIL PROTECTED] org.apache.bsf.util.BSFEventProcessor}. + * + * <pre>------------------------ Apache Version 2.0 license ------------------------- + * Copyright (C) 2001-2006 Rony G. Flatscher + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a> + * + * 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. + * ----------------------------------------------------------------------------- </pre> + * + * @author Rony G. Flatscher, but most of the code copied from org.apache.bsf.util.BSFEventProcessor by Sanjiva Weerawarana + * + * + * @see [EMAIL PROTECTED] org.apache.bsf.util.BSFEventProcessor} + * + */ +public class BSFEventProcessorReturningEventInfos implements EventProcessor { + BSFEngine engine; + BSFManager manager; + String filter; + String source; + int lineNo; + int columnNo; + Object script; + Object dataFromScriptingEngine; // ---rgf, 2006-02-24: data coming from the script engine, could be + // e.g. an object reference to forward event with received arguments to + + /** + * Package-protected constructor makes this class unavailable for + * public use. + * + * @param dataFromScriptingEngine this contains any object supplied by the scripting engine and gets + * sent back with the supplied script. This could be used e.g. for indicating which scripting + * engine object should be ultimately informed of the event occurrence. + */ + BSFEventProcessorReturningEventInfos (BSFEngine engine, BSFManager manager, String filter, + String source, int lineNo, int columnNo, Object script, Object dataFromScriptingEngine) + throws BSFException { + this.engine = engine; + this.manager = manager; + this.filter = filter; + this.source = source; + this.lineNo = lineNo; + this.columnNo = columnNo; + this.script = script; + this.dataFromScriptingEngine = dataFromScriptingEngine; + } + ////////////////////////////////////////////////////////////////////////// + // + // event is delegated to me by the adapters using this. inFilter is + // in general the name of the method via which the event was received + // at the adapter. For prop/veto change events, inFilter is the name + // of the property. In any case, in the event processor, I only forward + // those events if for which the filters match (if one is specified). + + + public void processEvent (String inFilter, Object[] evtInfo) { + try { + processExceptionableEvent (inFilter, evtInfo); + } catch (RuntimeException re) { + // rethrow this .. I don't want to intercept run-time stuff + // that can in fact occur legit + throw re; + } catch (Exception e) { + // should not occur + System.err.println ("BSFError: non-exceptionable event delivery " + + "threw exception (that's not nice): " + e); + e.printStackTrace (); + } + } + + ////////////////////////////////////////////////////////////////////////// + // + // same as above, but used when the method event method may generate + // an exception which must go all the way back to the source (as in + // the vetoableChange case) + + public void processExceptionableEvent (String inFilter, Object[] evtInfo) throws Exception { + +// System.err.println(this+": inFilter=["+inFilter+"], filter=["+filter+"]"); + if ((filter != null) && !filter.equals (inFilter)) { + // ignore this event + return; + } + + // run the script + // engine.exec (source, lineNo, columnNo, script); + + // create the parameter vectors for engine.apply() + Vector paramNames = new Vector(), paramValues = new Vector(); + + // parameter # 1 + // supply the parameters as an array object as sent to the event object listener + // (usually the first entry is the sent event object) + paramNames. add( "eventParameters" ); + paramValues.add( evtInfo ); + + // parameter # 2 + // supply the data object received from the scripting engine to be sent with the event + paramNames. add( "dataFromScriptingEngine" ); + paramValues.add( this.dataFromScriptingEngine ); // can be null as well + + // parameter # 3 + // event filter in place + paramNames. add( "inFilter" ); + paramValues.add( inFilter ); // event name that has occurred + + // parameter # 4 + // event filter in place + paramNames. add( "eventFilter" ); + paramValues.add( this.filter ); // can be null as well + + // parameter # 5 + // BSF manager instance (e.g. allows access to its registry) + paramNames. add( "BSFManager" ); + paramValues.add( this.manager ); + + engine.apply(source, lineNo, columnNo, this.script, paramNames, paramValues); + } +}
Property changes on: util/BSFEventProcessorReturningEventInfos.java ___________________________________________________________________ Name: svn:executable + * Name: svn:keywords + Author Date Rev Id URL Index: util/ReflectionUtils.java =================================================================== --- util/ReflectionUtils.java (revision 381339) +++ util/ReflectionUtils.java (working copy) @@ -70,6 +70,7 @@ * * @author Sanjiva Weerawarana * @author Joseph Kesselman + * @author Rony G. Flatscher (added Proxy-handling needed for OpenOffice.org 1.1.x and 2.0.x as of 2006-02-03) */ public class ReflectionUtils { @@ -98,25 +99,68 @@ throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException { + // find the event set descriptor for this event BeanInfo bi = Introspector.getBeanInfo (source.getClass ()); EventSetDescriptor esd = (EventSetDescriptor) findFeatureByName ("event", eventSetName, bi.getEventSetDescriptors ()); - if (esd == null) { - throw new IllegalArgumentException ("event set '" + eventSetName + - "' unknown for source type '" + - source.getClass () + "'"); - } + // get the class object for the event - Class listenerType = esd.getListenerType (); + Class listenerType = null; + int idx2mmm=0; + java.lang.reflect.Method mmm[]=null; // array object to store methods from Proxy-class reflected methods + if (esd == null) // no events found, maybe a proxy from OpenOffice.org? + { + if (java.lang.reflect.Proxy.class.isInstance(source)==true) // a Proxy class, hence reflect "manually" + { + mmm=source.getClass().getMethods(); // get all methods + for (idx2mmm=0; idx2mmm<mmm.length; idx2mmm++) + { + String methName=mmm[idx2mmm].getName(); + // looking for a method "add_XYZ_Listener(someEventClass) + if (methName.endsWith("Listener")==true) + { + String un=getUnqualifiedName(methName); + + if (un.startsWith("add")) // get first argument, which must be an Event class + { + String tmpName=un.substring(3, un.length()-8); // -lengthOf(("add"=3)+("Listener"=8)) + if (eventSetName.equalsIgnoreCase(tmpName)) + { + java.lang.Class params[]=mmm[idx2mmm].getParameterTypes(); + if (params.length>0) + { + listenerType=params[0]; // o.k. found ListenerClass + break; + } + } + } + } + } + } + + if (listenerType==null) // o.k. no listenerType found, throw up ... + { + throw new IllegalArgumentException ("event set '" + eventSetName + + "' unknown for source type '" + source.getClass () + "'"); + } + + } + else // ListenerType from EventSetDescriptor + { + listenerType=esd.getListenerType(); // get ListenerType class object from EventSetDescriptor + } + + + // find an event adapter class of the right type Class adapterClass = EventAdapterRegistry.lookup (listenerType); if (adapterClass == null) { - throw new IllegalArgumentException ("event adapter for listner type " + - "'" + listenerType + "' (eventset " + - "'" + eventSetName + "') unknown"); + throw new IllegalArgumentException ("event adapter for listener type " + + "'" + listenerType + "' (eventset " + + "'" + eventSetName + "') unknown"); } // create the event adapter and give it the event processor @@ -135,16 +179,77 @@ // in this case to support the source-side filtering. // // ** TBD **: the following two lines need to change appropriately - addListenerMethod = esd.getAddListenerMethod (); + if (mmm==null) + { + addListenerMethod = esd.getAddListenerMethod (); + } + else + { + addListenerMethod = mmm[idx2mmm]; + } args = new Object[] {adapter}; - } else { - addListenerMethod = esd.getAddListenerMethod (); + } + else + { + if (mmm==null) { + addListenerMethod = esd.getAddListenerMethod (); + } + else + { + addListenerMethod = mmm[idx2mmm]; + } args = new Object[] {adapter}; } addListenerMethod.invoke (source, args); } ////////////////////////////////////////////////////////////////////////// + + /** Compares two strings in a "relaxed" manner, i.e. + * tests case-insensitively, whether the second argument + * <code>haystack</code> ends with the first argument <code>endName</code> + * string. + * + * @param endName the string which should end <code>haystack</code> + * @param haystack the string to test <code>endName</code> against + * + * @return <code>true</code>, if <code>haystack</code> ends with the + * string <code>endName</code> (comparison carried out case-insensitively), + * <code>false</code> else + */ + static boolean compareRelaxed(String endName, String haystack) + { + int endNameLength=endName.length(), + tmpLength =haystack.length(); + + if (endNameLength>tmpLength) // interface endName is shorter than the sought of one + { + return false; + } + else if (endNameLength!=tmpLength) // cut off haystack from the right to match length of received endName + { + // 012345678 + // abc=3 x.y.z.abc=9 9-3=6 + haystack=haystack.substring(tmpLength-endNameLength); // cut off from the right + } + + return endName.equalsIgnoreCase(haystack); + } + ////////////////////////////////////////////////////////////////////////// + + /** Returns unqualified name (string after the last dot) from dotted string or string itself, if no dot in string. + * + * @param s String to extract unqualified name + * @return returns unqualified name or s, if no dot in string + */ + static String getUnqualifiedName(String s) + { + int lastPos=s.lastIndexOf('.'); // get position of last dot + return lastPos==-1 ? s : s.substring(lastPos+1) ; + } + ////////////////////////////////////////////////////////////////////////// + + /** * Create a bean using given class loader and using the appropriate * constructor for the given args of the given arg types. Index: util/EngineUtils.java =================================================================== --- util/EngineUtils.java (revision 381339) +++ util/EngineUtils.java (working copy) @@ -69,6 +69,7 @@ * * @author Sanjiva Weerawarana * @author Sam Ruby + * @author Rony G. Flatscher (added addEventListenerReturningEventInfos) */ public class EngineUtils { // the BSF class loader that knows how to load from the a specific @@ -129,7 +130,65 @@ } } + /** + * Add a script as a listener to some event coming out of an object. The + * first two args identify the src of the event and the event set + * and the rest identify the script which should be run when the event + * fires. The processing will use the engine's apply() method. + * + * @param bean event source + * @param eventSetName name of event set from event src to bind to + * @param filter filter for events + * @param engine BSFEngine which can run this script + * @param manager BSFManager of the above engine + * @param source (context info) the source of this expression (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute when the event occurs + * @param dataFromScriptingEngine + * this contains any object supplied by the scripting engine and gets sent + * back with the supplied script, if the event occurs. + * This could be used e.g. for indicating to the scripting engine which + * scripting engine object/routine/function/procedure + * should be ultimately informed of the event occurrence. + * + * @exception BSFException if anything goes wrong while running the script + */ + public static void addEventListenerReturningEventInfos ( Object bean, + String eventSetName, + String filter, + BSFEngine engine, + BSFManager manager, + String source, + int lineNo, + int columnNo, + Object script, + Object dataFromScriptingEngine + ) throws BSFException + { + BSFEventProcessorReturningEventInfos ep = + new BSFEventProcessorReturningEventInfos (engine, + manager, + filter, + source, + lineNo, + columnNo, + script, + dataFromScriptingEngine + ); + + try { + ReflectionUtils.addEventListener (bean, eventSetName, ep); + } catch (Exception e) { + e.printStackTrace (); + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "ouch while adding event listener: " + + e, e); + } + } + + /** * Finds and invokes a method with the given signature on the given * bean. The signature of the method that's invoked is first taken * as the types of the args, but if that fails, this tries to convert
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]