Package de.sciss.swingosc

Source Code of de.sciss.swingosc.SwingOSC$CmdMethod

/*
*  SwingOSC.java
*  SwingOSC
*
*  Copyright (c) 2005-2011 Hanns Holger Rutz. All rights reserved.
*
*  This software is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either
*  version 2, june 1991 of the License, or (at your option) any later version.
*
*  This software is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License (gpl.txt) along with this software; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*
*  For further information, please contact Hanns Holger Rutz at
*  contact@sciss.de
*
*
*  Changelog:
*    19-Aug-05  created
*    05-Sep-05  lot's of changes
*          - deferred messages to event thread
*          - added /n_get ; added nested propertyNames (i.e. "size.width")
*          - added special node names "GraphicsEnvironment", "Toolkit"
*          - -U option is now -u for consistency with scsynth
*    10-Nov-05  OSC command set completely reworked to make it as simple as possible
*          and allow any kind of extensions not limited to the awt/swing sphere.
*          special string<->object conversions are replaced by a nesting mechanism
*          using message-blobs as automatically generated by supercollider when nesting arrays
*    14-Feb-06  - fixed bug in /free
*          - mostly exchanged decodeMessageArgs with step-by-step
*            decoding using decodeMessageArg such as to provide a consistent causality
*            when arguments include OSCMessages with side effects
*          - added /field(r)
*          - all commands are now subclasses of BasicCmd, more efficient lookup
*          - removed pre-initialized object "toolkit" which would force the application
*            to become a swing app (in Mac OS X)
*    25-Mar-06  - new String based message nesting format
*          - uses new OSCClient class
*    08-Aug-06  - uses custom class loader ; added /classes command
*    01-Oct-06  uses new NetUtil version
*    18-Jan-08  fixed checkMethodArgs to put lower priority to Object assignment
*/
package de.sciss.swingosc;

import java.awt.EventQueue;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
//import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.sciss.app.BasicEvent;
import de.sciss.app.EventManager;
import de.sciss.net.OSCBundle;
import de.sciss.net.OSCChannel;
import de.sciss.net.OSCListener;
import de.sciss.net.OSCMessage;
import de.sciss.net.OSCPacket;
import de.sciss.net.OSCPacketCodec;
import de.sciss.net.OSCServer;
import de.sciss.net.OSCTransmitter;
//import de.sciss.util.DynamicURLClassLoader;
import de.sciss.util.URLClassLoaderManager;

/**
*  A one-in-all class launching an OSC widget server. Windows and
*  gadgets can be created by sending OSC commands to the server.
*  Action listeners can be registered and are notified about button
*  state changes.
*
@author    Hanns Holger Rutz
@version  0.66, 20-Oct-11
*
*  @todo    rendezvous option (jmDNS)
*  @todo    [NOT?] /n_notify (sending things like /n_go, n_end)
*  @todo    since we invoke runLater already, we could add timetag flavors easily
*  @todo    /import !!!check ImportText.xcodeproj!!! XXX
*/
public class SwingOSC
implements OSCListener, OSCProcessor, EventManager.Processor
{
  public static final double    VERSION      = 0.66;

  protected OSCServer        serv      = null;
 
//  private final List        collMessages  = Collections.synchronizedList( new ArrayList() );  // elements = IncomingMessage instances

  private final Map        mapClients    = new HashMap()// SocketAddr to Client
  protected final Map        globals      = new HashMap()// objectID to value
  private final Map        constants    = new HashMap()// objectID to value
 
  private static SwingOSC      instance;
  private SwingClient        currentClient  = null;
 
  protected final Map        oscCmds      = new HashMap( 20 )// OSC-command-name to OSCProcessor
  private final EventManager    elm;
 
  private static int        numTraceLines  = 3;

//  private final DynamicURLClassLoader classLoaderMgr;
  protected final URLClassLoaderManager classLoaderMgr;
 
    public static void main( String[] args )
  {
      System.exit( launch( args ));
  }
   
    /**
     *   Launches the server with given command line options.
     *
     *  @param  args  the array of command line options, e.g.
     *           <code>-t 57111</code>.
     *  @return      the return code (0 for success, 1 for failure)
     */
    public static int launch( String[] args )
    {
    String          arg;
    int            port    = 0;
    boolean          loopBack  = false;
    boolean          initSwing  = false;
    InetSocketAddress    hello    = null;
    int            bufSize    = 65536;
    String          protocol  = OSCChannel.UDP;
    int            i, j;
 
    for( i = 0; i < args.length; i++ ) {
      arg = args[ i ];
      if( arg.equals( "-u" )) {
        i++;
        if( i < args.length ) {
          try {
            port = Integer.parseInt( args[ i ]);
          }
          catch( NumberFormatException e1 ) {
            printException( e1, "-u" );
          }
        } else {
          System.out.println( "-u option requires port specification! (-u <portNum>)" );
        }
       
      } else if( arg.equals( "-t" )) {
          i++;
          if( i < args.length ) {
            try {
              port     = Integer.parseInt( args[ i ]);
              protocol  = OSCChannel.TCP;
            }
            catch( NumberFormatException e1 ) {
              printException( e1, "-t" );
            }
          } else {
            System.out.println( "-t option requires port specification! (-t <portNum>)" );
          }
         
      } else if( arg.equals( "-L" )) {
        loopBack  = true;
     
      } else if( arg.equals( "-b" )) {
        i++;
        if( i < args.length ) {
          try {
            bufSize   = Integer.parseInt( args[ i ]);
          }
          catch( NumberFormatException e1 ) {
            printException( e1, "-b" );
          }
        } else {
          System.out.println( "-b option requires size specification! (-b <size>)" );
        }
       
      } else if( arg.equals( "-i" )) {
        initSwing  = true;
     
      } else if( arg.equals( "-h" )) {
        i++;
        if( i < args.length ) {
          j = args[ i ].indexOf( ':' );
          try {
            hello = new InetSocketAddress( args[ i ].substring( 0, j ),
                Integer.parseInt( args[ i ].substring( j + 1 )));
          }
          catch( IndexOutOfBoundsException e1 ) {
            printException( e1, "-h" );
          }
          catch( NumberFormatException e1 ) {
            printException( e1, "-h" );
          }
          catch( IllegalArgumentException e1 ) {
            printException( e1, "-h" );
          }
        } else {
          System.out.println( "-h option requires address specification! (-h <hostName:port>)" );
        }
     
      } else if( arg.equals( "--help" )) {
        System.out.println( "SwingOSC command line options:\n\n" +
                  "   -u <portNum>    at which UDP port the server shall receive.\n"+
                  "                   may be omitted (a number is automatically\n"+
                  "                   generated by the system).\n\n"+
                  "   -t <portNum>    at which TCP port the server shall receive.\n"+
                  "   -L              use the loopback address for reception.\n"+
                  "                   this restricts access to the local machine.\n\n"+
                  "   -b <size>       maximum size of OSC messages (default 65536).\n"+
                  "   -i              initialize GUI environment upon startup,\n"+
                  "                   i.e. create menubar and dock icon on mac os\n"+
                  "   -h <host:port>  send /swing hello message to specified address" );
       
        return 0;
       
      } else {
        System.out.println( "Ignoring unknown option " + args[ i ] +
                  "\nUse --help for a list of options." );
      }
    }
 
    new SwingOSC();
    if (!loopBack) {
      System.out.println("\n WARNING : your system is very vulnerable\n"
          + "           when connected to a network!\n"
          + "           using SwingOSC, it is easy to\n"
          + "           hijack the machine or even erase\n"
          + "           the hardisk.\n");
    }

    try {
      synchronized( instance ) {
        instance.start( protocol, port, loopBack, bufSize, initSwing, hello );
        instance.wait();
        return instance.quit();
      }
    }
    catch( InterruptedException e2 ) {
      System.out.println( e2.getClass().getName() + " : " + e2.getLocalizedMessage() );
    }
    catch( IOException e1 ) {
      printException( e1, instance.getClass().getName() );
    }
    return 1;
  }

  /*
   * @synchronization needn't be called in event thread
   */
    public SwingOSC()
  {
      instance  = this;
//      classLoaderMgr = new DynamicURLClassLoader( this.getClass().getClassLoader() );
      classLoaderMgr = new URLClassLoaderManager( this.getClass().getClassLoader() );
     
       // helper symbols
    constants.put( "null", null );
    constants.put( "brko", "[" );
    constants.put( "brkc", "]" );
    constants.put( "swing", this );
//    constants.put( "toolkit", Toolkit.getDefaultToolkit() );
   
    // OSC commands (they register themselves)
    new CmdQuit( this );
    new CmdNew();
    new CmdRef();
    new CmdLocal();
    new CmdGlobal();
    new CmdSet();
    new CmdGet();
    new CmdMethod();
    new CmdMethodR();
    new CmdField();
    new CmdFieldR();
    new CmdFree();
    new CmdArray();
    new CmdQuery();
    new CmdPrint();
    new CmdDumpOSC();
    new CmdClasses();
   
//    try {
//      final Class[] classes = AudioFileFormat.class.getClasses();
//      for( int i = 0; i < classes.length; i++ ) {
//        System.out.println( "i = "+i+"; name = "+classes[i].getName() );
//      }
//    }
//    catch( Exception e ) {
//      System.out.println( e );
//    }
   
    elm = new EventManager( this );
  }
   
    public static boolean isMacOS() {
        return System.getProperty( "os.name" ).indexOf( "Mac" ) >= 0;
    }
  
  public static boolean isWindows() {
      return System.getProperty( "os.name" ).indexOf( "Windows" ) >= 0;
    }
   
    public static void setNumTraceLines( int num )
    {
      numTraceLines = num;
    }
 
  public static int notNull( Object o )
  {
    return o == null ? 0 : 1;
  }
 
  public static SwingOSC getInstance()
  {
    return instance;
  }
 
  public SwingClient getCurrentClient()
  {
    return currentClient;
  }
 
  public OSCPacketCodec getDefaultCodec()
  {
    return OSCPacketCodec.getDefaultCodec();
  }
 
  public void setReplyAddress( SocketAddress addr )
  {
    currentClient.setReplyAddress( addr );
  }
 
  public void setReplyPort( int port )
  {
    currentClient.setReplyPort( port );
  }
 
  public double getVersion()
  {
    return VERSION;
  }
 
  public InetSocketAddress getLocalAddress()
  throws IOException
  {
    return serv.getLocalAddress();
  }
 
  public String getProtocol()
  {
    return serv.getProtocol();
  }
 
  public void send( OSCPacket p, SocketAddress addr )
  throws IOException
  {
    serv.send( p, addr );
  }
 
  public void start( String protocol, int port, boolean loopBack, int bufSize,
             boolean initSwing, InetSocketAddress helloAddr )
  throws IOException
  {
    final InetSocketAddress  ourAddress;
    final String       ourHost;
    final int        ourPort;

    serv    = OSCServer.newUsing( protocol, port, loopBack );
    serv.setBufferSize( bufSize );
    ourAddress  = serv.getLocalAddress();
//    ourHost    = ourAddress.getHostName();
    ourHost    = ourAddress.getAddress().getHostAddress();
    ourPort    = ourAddress.getPort();
    serv.addOSCListener( this );
 
    final String vStr = String.valueOf( VERSION + 0.001 ); // use double prec!
    System.out.println( "SwingOSC v" + vStr.substring( 0, vStr.length() - 1 ) +". receiving " + protocol.toUpperCase() +
              " at address " + ourHost + ":" + ourPort );
//    serv.dumpIncomingOSC( 3, System.out );
    serv.getCodec().setSupportMode( (OSCPacketCodec.MODE_GRACEFUL & ~OSCPacketCodec.MODE_WRITE_DOUBLE_AS_FLOAT) | OSCPacketCodec.MODE_WRITE_DOUBLE );
    serv.start();
    if( initSwing ) {
      // calling one of AWT's method will make Mac OS X recognize we're
      // a GUI app and hence launch the screen menu bar and put an icon in the dock
      // ; since this takes a moment it can be useful to do it at startup
      // and not lazily when the first OSC message comes in
      EventQueue.invokeLater( new Runnable() { public void run() { /* empty */ }});
    }
    if( helloAddr != null ) {
//      serv.send( new OSCMessage( "/swing", new Object[] {
//          "hello", ourHost, new Integer( ourPort )}),
//          hello );
      final OSCMessage helloMsg = new OSCMessage( "/swing", new Object[] {
        "hello", ourHost, new Integer( ourPort ), protocol });
//      final OSCTransmitter helloTrns = OSCTransmitter.newUsing( OSCChannel.UDP );
      final OSCTransmitter helloTrns = OSCTransmitter.newUsing( OSCChannel.UDP, 0, helloAddr.getAddress().isLoopbackAddress() );
      helloTrns.connect();
      helloTrns.send( helloMsg, helloAddr );
      helloTrns.dispose();
    }
  }
 
  private int quit()
  {
    if( serv != null ) serv.dispose();
    return 0;
  }

  public static void printException( Throwable e, String opName )
  {
    System.out.println( opName + " : " + e.getClass().getName() + " : " + e.getLocalizedMessage() );
        final StackTraceElement[] trace = e.getStackTrace();
        for( int i = 0; i < Math.min( numTraceLines, trace.length ); i++ ) {
            System.out.println( "\tat " + trace[ i ]);
        }
        if( trace.length > numTraceLines ) {
            System.out.println( "\t..." );
        }
  }

  public static void printException( Throwable e, OSCMessage msg )
  {
    printException( e, msg.getName() );
  }

  private static void printWarning( OSCMessage msg, String text )
  {
    System.out.println( "WARNING " + msg.getName() + " " + text );
  }

  protected static void printFailed( OSCMessage msg, String text )
  {
    System.out.println( "FAILURE " + msg.getName() + " " + text );
  }

  protected static void printArgMismatch( OSCMessage msg )
  {
    printFailed( msg, "Argument mismatch" );
  }

  protected static void printWrongArgCount( OSCMessage msg )
  {
    printWarning( msg, "Called with illegal arg count" );
  }

  protected static void printNotFound( OSCMessage msg, Object id )
  {
    printFailed( msg, "Object not found : " + id );
  }

  public void messageReceived( OSCMessage msg, SocketAddress addr, long when )
  {
//    collMessages.add( new IncomingMessage( msg, addr, when ));
//    EventQueue.invokeLater( this );
    elm.dispatchEvent( new IncomingMessage( msg, addr, when ));
  }

  // called from the event thread
  // when new messages have been queued
  public void processEvent( BasicEvent e )
  {
    final IncomingMessage  imsg = (IncomingMessage) e;
    SwingClient        c;
 
    c    = (SwingClient) mapClients.get( imsg.addr );
    if( c == null ) {
      c = new SwingClient( this, imsg.addr );
      mapClients.put( imsg.addr, c );
    }
    processMessage( imsg.msg, c );
  }

  protected Object[] decodeMessageArgs( OSCMessage msg, SwingClient c )
  throws IOException
  {
    final Object[] result = new Object[ msg.getArgCount() ];
   
    for( int idx = 0; idx < result.length; idx++ ) {
      result[ idx ] = decodeMessageArg( msg, c, idx );
//      o = msg.getArg( i );
//      if( o instanceof byte[] ) {
//        o = OSCPacket.decode( ByteBuffer.wrap( (byte[]) o), null );
//      }
//      if( o instanceof OSCMessage ) {
//        o = processMessage( (OSCMessage) o, addr );
//      } else if( o instanceof OSCBundle ) {
//        o = processBundle( (OSCBundle) o, addr );
//      }
//      result[ i ] = o;
//System.out.println( "result[ "+i+" ] = "+o );
    }
   
    return result;
  }

  protected Object decodeMessageArg( OSCMessage msg, SwingClient c, int idx )
  throws IOException
  {
    Object      o;
//    OSCPacketCodec  codec;
   
    o = msg.getArg( idx );
    if( o instanceof byte[] ) {
//      o = c.getCodec().decode( ByteBuffer.wrap( (byte[]) o) );
      o  = serv.getCodec( c.getReplyAddress() ).decode( ByteBuffer.wrap( (byte[]) o) );
    }
    if( o instanceof OSCMessage ) {
      o = processMessage( (OSCMessage) o, c );
    } else if( o instanceof OSCBundle ) {
      o = processBundle( (OSCBundle) o, c );
    }
    return o;
  }

  // best possible match : numArgs * 4
  protected static int checkMethodArgs( Class[] signature, Object[] msgArgs, int msgArgOff, Object[] result )
  {
    Class  type;
    Object  msgArg;
    int    match  = 0;
 
    for( int j = 0, k = msgArgOff; j < signature.length; j++, k++ ) {
      type    = signature[ j ];
      msgArg  = msgArgs[ k ];
      if( msgArg == null ) {
        result[ j ] = msgArg;
        match     += 4;
      } else if( type.isPrimitive() ) {
        if( msgArg instanceof Number ) {
          if( type.equals( Boolean.TYPE )) {
            result[ j ] = new Boolean( ((Number) msgArg).intValue() != 0 );
            match += (msgArg instanceof Byte) ? 3 : ((msgArg instanceof Integer) ? 2 : 1);
          } else if( type.equals( Integer.TYPE )) {
            if( msgArg instanceof Integer ) {
              result[ j ] = msgArg;
              match += 4;
            } else {
              result[ j ] = new Integer( ((Number) msgArg).intValue() );
              match += ((msgArg instanceof Long) || (msgArg instanceof Byte) ||
                    (msgArg instanceof Short)) ? 3 : 1;
            }
          } else if( type.equals( Long.TYPE )) {
            if( msgArg instanceof Long ) {
              result[ j ] = msgArg;
              match += 4;
            } else {
              result[ j ] = new Long( ((Number) msgArg).longValue() );
              match += ((msgArg instanceof Integer) || (msgArg instanceof Byte) ||
                    (msgArg instanceof Short)) ? 3 : 1;
            }
          } else if( type.equals( Float.TYPE )) {
            if( msgArg instanceof Float ) {
              result[ j ] = msgArg;
              match += 4;
            } else {
              result[ j ] = new Float( ((Number) msgArg).floatValue() );
              match += (msgArg instanceof Double) ? 3 : 1;
            }
          } else if( type.equals( Double.TYPE )) {
            if( msgArg instanceof Double ) {
              result[ j ] = msgArg;
              match += 4;
            } else {
              result[ j ] = new Double( ((Number) msgArg).doubleValue() );
              match += (msgArg instanceof Float) ? 3 : 1;
            }
          } else if( type.equals( Byte.TYPE )) {
            if( msgArg instanceof Byte ) {
              result[ j ] = msgArg;
              match += 4;
            } else {
              result[ j ] = new Byte( ((Number) msgArg).byteValue() );
              match += ((msgArg instanceof Long) || (msgArg instanceof Integer) ||
                    (msgArg instanceof Short)) ? 3 : 1;
            }
          } else if( type.equals( Short.TYPE )) {
            if( msgArg instanceof Short ) {
              result[ j ] = msgArg;
              match += 4;
            } else {
              result[ j ] = new Short( ((Number) msgArg).shortValue() );
              match += ((msgArg instanceof Long) || (msgArg instanceof Integer) ||
                    (msgArg instanceof Byte)) ? 3 : 1;
            }
          } else {
            return -1;
          }
        } else if( type.equals( Boolean.TYPE )) {
          if( msgArg instanceof Boolean ) {
            result[ j ] = msgArg;
            match     += 4;
          } else {
            return -1;
          }
        } else if( type.equals( Character.TYPE )) {
          if( (msgArg instanceof String) && ((String) msgArg).length() == 1 ) {
            result[ j ] = new Character( ((String) msgArg).charAt( 0 ));
            match += 3;
          } else {
            return -1;
          }
        }
      } else if( type.equals( String.class )) {
        if( msgArg instanceof String ) {
          result[ j = msgArg;
          match      += 4;
        } else {
          result[ j = msgArg.toString();
        }
      // direct assignment always preferred
      } else if( type.isAssignableFrom( msgArg.getClass() )) {
        result[ j ] = msgArg;
        match     += type.equals( Object.class ) ? 2 : 4;
      } else { // not assignable
        return -1;
      }
    } // for signature types
   
    return match;
  }   

  protected Object getObject( Object id )
  {
    Object result  = null;
   
    result    = globals.get( id );
    if( result != null ) return result;
    result    = constants.get( id );
    if( result == null ) {
      final String className = id.toString();
      if( (className.length() > 0) &&
        (Character.isUpperCase( className.charAt( 0 )) || (className.indexOf( '.' ) >= 0))) {

        // ok, maybe it's a class reference
        try {
          result  = Class.forName( className, true, classLoaderMgr.getCurrentLoader() );
        }
        catch( LinkageError e ) {
          printException( e, "getObject" );
        }
        catch( ClassNotFoundException e ) {
          printException( e, "getObject" );
        }
      }
    }
   
    return result;
  }

  protected Object setProperty( OSCMessage msg, String propName, Object propValue, Object root )
  throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
  {
    final int    subIdx      = propName.indexOf( '.' );
    final boolean  hasSub      = subIdx > 0;
    final String    setterName, getterSuffix;
    final Object[]  methodArgs;
    final Method[]  methods;
    final Object    o;
    final Class    c        =  root instanceof Class ? (Class) root : root.getClass();
    Method      m;
    Object      methodArg;
    Class[]      paramTypes;
    Class      paramClass;

    // ----------------- call getter first to find the target object -----------------
    if( hasSub ) {
      getterSuffix  = Character.toUpperCase( propName.charAt( 0 )) + propName.substring( 1, subIdx );
      try {
        m  = c.getMethod( "get" + getterSuffix, (Class[]) null );
      }
      catch( NoSuchMethodException e11 ) {  // ok, retry with "isXY"
        m  = c.getMethod( "is" + getterSuffix, (Class[]) null );
      }
      o = m.invoke( root, (Object[]) null );
      return setProperty( msg, propName.substring( subIdx + 1 ), propValue, o );

    // ----------------- find setter method and invoke it -----------------
    } else {
      if( propName.length() > 0 ) {
        setterName    = "set" + Character.toUpperCase( propName.charAt( 0 )) + propName.substring( 1 );
      } else {
        setterName    = "set";
      }
      methods        = c.getMethods();
      methodArgs      = new Object[ 1 ];
      for( int i = 0; i < methods.length; i++ ) {
        if( methods[ i ].getName().equals( setterName )) {
          paramTypes  = methods[ i ].getParameterTypes();
          methodArg  = null;
          if( (paramTypes.length == 1) && (propValue != null) ) {
            paramClass  = paramTypes[ 0 ];
            if( paramClass.isAssignableFrom( propValue.getClass() )) {
              methodArg = propValue;
            } else if( paramClass.equals( String.class )) {
              methodArg = propValue.toString();
            } else if( paramClass.isPrimitive() ) {
              if( paramClass.equals( Boolean.TYPE )) {
                if( propValue instanceof Boolean ) {
                  methodArg = propValue;
                } else if( propValue instanceof Number ) {
                  methodArg = new Boolean( ((Number) propValue).intValue() != 0 );
                }
              } else if( paramClass.equals( Integer.TYPE )) {
                if( propValue instanceof Number ) {
                  methodArg = new Integer( ((Number) propValue).intValue() );
                }
              } else if( paramClass.equals( Long.TYPE )) {
                if( propValue instanceof Number ) {
                  methodArg = new Long( ((Number) propValue).longValue() );
                }
              } else if( paramClass.equals( Float.TYPE )) {
                if( propValue instanceof Number ) {
                  methodArg = new Float( ((Number) propValue).floatValue() );
                }
              } else if( paramClass.equals( Double.TYPE )) {
                if( propValue instanceof Number ) {
                  methodArg = new Double( ((Number) propValue).doubleValue() );
                }
              } else if( paramClass.equals( Byte.TYPE )) {
                if( propValue instanceof Number ) {
                  methodArg = new Byte( ((Number) propValue).byteValue() );
                }
              } else if( paramClass.equals( Short.TYPE )) {
                if( propValue instanceof Number ) {
                  methodArg = new Short( ((Number) propValue).shortValue() );
                }
              } else if( paramClass.equals( Character.TYPE )) {
                if( propValue instanceof Character ) {
                  methodArg = propValue;
                } else if( (propValue instanceof String) && ((String) propValue).length() == 1 ) {
                  methodArg = new Character( ((String) propValue).charAt( 0 ));
                }
              }
            }
          }
          if( (methodArg != null) || (propValue == null) ) {
            methodArgs[ 0 ] = methodArg;
            return methods[ i ].invoke( root, methodArgs );
          }           
        }
      }
      printFailed( msg, "No matching setter method for " + propName );
      return null;
    }
  }

  protected Object getProperty( String propName, Object root )
  throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
  {
    final int    subIdx      = propName.indexOf( '.' );
    final boolean  hasSub      = subIdx > 0;
    final String  getterSuffix  = Character.toUpperCase( propName.charAt( 0 )) + propName.substring( 1, hasSub ? subIdx : propName.length() );
    final Object  o;
    final Class    c        =  root instanceof Class ? (Class) root : root.getClass();
    Method      m;

    try {
      m  = c.getMethod( "get" + getterSuffix, (Class[]) null );
    }
    catch( NoSuchMethodException e11 ) {  // ok, retry with "isXY"
      m  = c.getMethod( "is" + getterSuffix, (Class[]) null );
    }
   
    o = m.invoke( root, (Object[]) null );
   
    if( hasSub ) {
      return getProperty( propName.substring( subIdx + 1 ), o );
    } else {
      if( o instanceof Boolean ) {
        return new Integer( ((Boolean) o).booleanValue() ? 1 : 0 );
      } else {
        return o;
      }
    }
  }

  protected Object getProperties( Object o, Object[] replyArgs, int replyOff,
                  Object[] propNames, int propOff, int numArgs )
  throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
  {
    Object  result    = null;
    String  propName;
 
    for( int i = 0; i < numArgs; i++ ) {
      propName          = propNames[ propOff++ ].toString();
      replyArgs[ replyOff++ = propName;
      result          = getProperty( propName, o );
      replyArgs[ replyOff++ = result;
    }
   
    return result;
  }

  protected Object invokeField( OSCMessage msg, Object o, String name )
  {
    final Class c = o instanceof Class ? (Class) o : o.getClass();

    try {
      return( c.getField( name ).get( o ));
    } catch( IllegalAccessException e ) {
      printException( e, msg );
    } catch( IllegalArgumentException e ) {
      printException( e, msg );
    } catch( NullPointerException e) {
      printException( e, msg );
    } catch( ExceptionInInitializerError e ) {
      printException( e, msg );
    } catch( NoSuchFieldException e ) {
      printException( e, msg );
    } catch( SecurityException e ) {
      printException( e, msg );
    }
    return null;
  }
 
  /**
   *   Fines a best matching method for a given argument set.
   *
   *  @param   o      the instance which will be searched for the method.
   *            if o is a Class, a static method
   *            will be looked up.
   *  @param   methodName  the name of the method to invoke.
   *  @param   methodArgs  the arguments to parse to the method invocation. according to
   *            the heuristics used, a best matching method is tried to be found
   *            by checking all methods with methodName and the given number
   *            of arguments. For example, an Integer in methodArgs might be translated
   *            into a primitive int, or even a float.
   *  @return  the result of the invoked method.
   *
   *  @throws NoSuchMethodException  if no method could be found for the given set of arguments
   */
  protected Method findBestMethod( Object o, String methodName, Object[] methodArgs, Object[] methodCArgs )
  throws NoSuchMethodException
  {
    final Class      c      = o instanceof Class ? (Class) o : o.getClass();
    final int      numArgs    = methodArgs.length;

    final Method[]    methods;
    Object[]      methodTArgs;
    Class[]        types;
    Method        method, bestMethod;
    int          match, bestMatch;
    final int      bestPossible;

    if( numArgs == 0 ) {  // the easy way
      return c.getMethod( methodName, (Class[]) null );
    } else {
      methods      = c.getMethods();
      methodTArgs    = new Object[ numArgs ]// test converted args
      bestMatch    = -1;
      bestMethod    = null;
      bestPossible  = numArgs << 2;
      for( int i = 0; i < methods.length; i++ ) {
        method  = methods[ i ];
        if( !method.getName().equals( methodName )) continue;
        types  = method.getParameterTypes();
        if( types.length != numArgs ) continue;

        match  = checkMethodArgs( types, methodArgs, 0, methodTArgs );
        if( match > bestMatch ) {
          bestMatch  = match;
          bestMethod  = method;
          System.arraycopy( methodTArgs, 0, methodCArgs, 0, numArgs );
          if( bestMatch == bestPossible ) {
            break;
          }
//          if( i + 1 < methods.length ) methodCArgs = new Object[ numArgs ];
        }
      } // for methods

      if( bestMethod != null ) return bestMethod;
      else throw new NoSuchMethodException( "No matching method signature for " + methodName );
    }
  }
 
  /**
   *   Invokes a method by finding the best match for a given argument set.
   *
   *  @param   o      the object on which to call the method. the method must be
   *            an instance method of o, or if o is a Class, a static method
   *            will be looked up.
   *  @param   methodName  the name of the method to invoke.
   *  @param   methodArgs  the arguments to parse to the method invocation. according to
   *            the heuristics used, a best matching method is tried to be found
   *            by checking all methods with methodName and the given number
   *            of arguments. For example, an Integer in methodArgs might be translated
   *            into a primitive int, or even a float.
   *  @return  the result of the invoked method.
   *
   *  @throws NoSuchMethodException  if no method could be found for the given set of arguments
   *  @throws LinkageError
   *  @throws SecurityException
   *  @throws IllegalAccessException
   *  @throws IllegalArgumentException
   *  @throws InvocationTargetException
   *  @throws ClassCastException
   */
  protected Object invokeMethod( Object o, String methodName, Object[] methodArgs )
  throws  LinkageError, SecurityException, NoSuchMethodException, IllegalAccessException,
      IllegalArgumentException, InvocationTargetException, ClassCastException
  {
    final Object[]     methodCArgs  = new Object[ methodArgs.length ];
    final Method     method    = findBestMethod( o, methodName, methodArgs, methodCArgs );
    return method.invoke( o, methodCArgs );
  }
 
  protected Object invokeMethod( OSCMessage msg, Object o, String methodName, Object[] msgArgs )
  {
    try {
      return invokeMethod( o, methodName, msgArgs );
    }
    catch( LinkageError e ) {
      printException( e, msg );
    }
    catch( SecurityException e ) {
      printException( e, msg );
    }   
    catch( NoSuchMethodException e ) {
      printException( e, msg );
    }
    catch( IllegalAccessException e ) {
      printException( e, msg );
    }
    catch( IllegalArgumentException e ) {
      printException( e, msg );
    }
    catch( InvocationTargetException e ) {
      printException( e.getTargetException(), msg );
    }
    catch( ClassCastException e ) {
      printException( e, msg );
    }
   
    return null;
  }
 
  private Object processBundle( OSCBundle bndl, SwingClient c )
  {
    OSCPacket  p;
    Object    result = null;
   
    for( int j = 0; j < bndl.getPacketCount(); j++ ) {
      p = bndl.getPacket( j );
      if( p instanceof OSCMessage ) {
        result = processMessage( (OSCMessage) p, c );
      } else {
        result = processBundle( (OSCBundle) p, c );
      }
    }
    return result;
  }

//   ------------------- OSCProcessor interface  -------------------

  // all messages are deferred to the swing event thread
  // for obvious reasons. also frees us from thread concurrency issues
  public Object processMessage( OSCMessage msg, SwingClient c )
  {
    try // catch everything because if a runtime exception occurs, OSC receiver thread dies
      final OSCProcessor cmd = (OSCProcessor) oscCmds.get( msg.getName() );

      currentClient = c;
     
      if( cmd != null ) {
        return cmd.processMessage( msg, c );
      } else {
        printFailed( msg, "Command not found" );
      }
    }
    catch( Exception e1 ) {
      printException( e1, "" );
    }
    return null;
  }

// ------------------- internal classes  -------------------

  private static class IncomingMessage
  extends BasicEvent
  {
    protected final OSCMessage    msg;
    protected final SocketAddress  addr;
   
    protected IncomingMessage( OSCMessage msg, SocketAddress addr, long when )
    {
      super( addr, 0, when );
      this.msg   = resolveMessage( msg );
      this.addr  = addr;
    }
   
    public boolean incorporate( BasicEvent e ) { return false; }

    private static OSCMessage resolveMessage( OSCMessage msg )
    {
      Object o;
     
      for( int idx = 0; idx < msg.getArgCount(); idx++ ) {
        o = msg.getArg( idx );
        if( o.equals( "[" )) {
          final List newArgs = new ArrayList();
          for( int j = 0; j < idx; j++ ) {
            newArgs.add( msg.getArg( j ));
          }
          idx = resolveNested( msg, newArgs, idx );
          while( idx < msg.getArgCount() ) {
            o = msg.getArg( idx );
            if( o.equals( "[" )) {
              idx = resolveNested( msg, newArgs, idx );
            } else {
              newArgs.add( o );
              idx++;
            }
          }
          return new OSCMessage( msg.getName(), newArgs.toArray() );
        }
      }
      return msg;
    }
   
    private static int resolveNested( OSCMessage msg, List parentArgs, int idx )
    {
      idx++;  // skip opening bracket
      final List     subArgs  = new ArrayList();
      final String  subName  = msg.getArg( idx++ ).toString();
      Object      o;
      do {
        o = msg.getArg( idx );
        if( o.equals( "[" )) {
          idx = resolveNested( msg, subArgs, idx );
        } else if( o.equals( "]" )) {
          parentArgs.add( new OSCMessage( subName, subArgs.toArray() ));
          idx++;
          return idx;
        } else {
          subArgs.add( o );
          idx++;
        }
      } while( true );
//      throw new ArrayIndexOutOfBoundsException( idx );
    }
  }

  private abstract class BasicCmd
  implements OSCProcessor
  {
    protected BasicCmd( String name )
    {
      oscCmds.put( name, this );
    }
  }
 
  /**
   *  Command: /quit
   */
  private class CmdQuit
  extends BasicCmd
  {
    private final Object sync;
   
    protected CmdQuit( Object sync )
    {
      super( "/quit" );
      this.sync = sync;
    }
   
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      synchronized( sync ) {
        sync.notifyAll();
        return null;
      }
    }
  } // class CmdQuit
 
  /**
   *  Command: /new, String <className>, [ Object <arg1> ... ]
   */
  private class CmdNew
  extends BasicCmd
  {
    protected CmdNew()
    {
      super( "/new" );
    }

    /**
     *  Note that message args are processed stricly after another
     *  which means for example that the className statement, if it is an OSCMessage
     *  and not a constant string, will not be able to access
     *  object bindings made by later arguments.
     *
     *  @param  msg    the /new message
     *  @param  c    the client
     *  @return      the newly created object
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      try {
  //      final Object[]    msgArgs    = decodeMessageArgs( msg, addr );
        final Object[]    msgArgs;
        final String      className;
        final Class      theClass;
        final int      numArgs    = msg.getArgCount() - 1;
        final Constructor[]  cons;
        Object[]        consArgs, bestArgs;
        Class[]        types;
        Constructor      bestCons;
        int          match, bestMatch;
        final int      bestPossible;
 
  //      if( msgArgs.length >= 1 ) {
        if( numArgs >= 0 ) {
          className  = decodeMessageArg( msg, c, 0 ).toString();
          theClass  = Class.forName( className, true, classLoaderMgr.getCurrentLoader() );
          if( numArgs == 0 ) {  // the easy way
            return theClass.newInstance();
          } else {
            cons      = theClass.getConstructors();
            consArgs    = new Object[ numArgs ];
            bestMatch  = -1;
            bestCons    = null;
            bestArgs    = null;
            bestPossible  = numArgs << 2;
            msgArgs    = decodeMessageArgs( msg, c );
            for( int i = 0; i < cons.length; i++ ) {
              types  = cons[ i ].getParameterTypes();
              if( types.length != numArgs ) continue;
              match  = checkMethodArgs( types, msgArgs, 1, consArgs );
  //System.out.print( "checking cons with " );
  //for( int jjj = 0; jjj < types.length; jjj++ ) {
  //  System.out.print( types[ jjj ].getName() + "; " );
  //}
  //System.out.println( " -> match "+ match );
              if( match > bestMatch ) {
                bestMatch  = match;
                bestCons  = cons[ i ];
                bestArgs  = consArgs;
                if( bestMatch == bestPossible ) {
  //                System.out.println( "... best possible" );
                  break;
                }
                if( i + 1 < cons.length ) consArgs = new Object[ numArgs ];
              }
            } // for constructors
 
            if( bestCons != null ) {
              return bestCons.newInstance( bestArgs );
            }
          }
         
          printFailed( msg, "No matching constructor for " + className );
        } else {
          printArgMismatch( msg );
        }
      }
      catch( LinkageError e ) {
        printException( e, msg );
      }
      catch( ClassNotFoundException e ) {
        printException( e, msg );
      }
      catch( SecurityException e ) {
        printException( e, msg );
      }   
      catch( IllegalAccessException e ) {
        printException( e, msg );
      }
      catch( InstantiationException e ) {
        printException( e, msg );
      }
      catch( IllegalArgumentException e ) {
        printException( e, msg );
      }
      catch( InvocationTargetException e ) {
        printException( e.getTargetException(), msg );
      }
      catch( ClassCastException e ) {
        printException( e, msg );
      }
     
      return null;
    }
  } // class CmdNew

  /**
   *  Command: /ref, String <objectID>
   */
  private class CmdRef
  extends BasicCmd
  {
    protected CmdRef()
    {
      super( "/ref" );
    }
 
    /**
     *  @param  msg    the /ref message
     *  @param  c    the client
     *  @return      the referred object
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      if( msg.getArgCount() == 1 ) {
        return c.getObject( decodeMessageArg( msg, c, 0 ));
      } else {
        printArgMismatch( msg );
        return null;
      }
    }
  } // class CmdRef
 
  /**
   *  Command: /local, [ String <objectID1>, Object <value1> ... ]
   */
  private class CmdLocal
  extends BasicCmd
  {
    protected CmdLocal()
    {
      super( "/local" );
    }
   
    /**
     *  Note that each message argument is parsed strictly after another
     *  so that later arguments can make use of assignments made by earlier arguments.
     *
     *  @param  msg    the /local message
     *  @param  c    the client
     *  @return      the last objectID (for convenience)
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]  msgArgs  = decodeMessageArgs( msg, addr );
      final int    numArgs  = msg.getArgCount(); // msgArgs.length;
      Object      id    = null;
      Object      val;
 
      if( (numArgs & 1) != 0 ) {
        printWrongArgCount( msg );
      }
     
      for( int i = 0; i < numArgs; ) {
        id  = decodeMessageArg( msg, c, i++ );
        val  = decodeMessageArg( msg, c, i++ );
        c.locals.put( id, val );
      }
 
      return id;
  //    return numArgs < 2 ? null : msgArgs[ numArgs - 2 ];
    }
  } // class CmdLocal
 
  /**
   *  Command: /global, [ String <objectID1>, Object <value1> ... ]
   */
  private class CmdGlobal
  extends BasicCmd
  {
    protected CmdGlobal()
    {
      super( "/global" );
    }
   
    /**
     *  Note that each message argument is parsed strictly after another
     *  so that later arguments can make use of assignments made by earlier arguments.
     *
     *  @param  msg    the /global message
     *  @param  c    the client
     *  @return      the last objectID (for convenience)
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]  msgArgs  = decodeMessageArgs( msg, addr );
      final int    numArgs  = msg.getArgCount(); // msgArgs.length;
      Object      id    = null;
      Object      val;
 
      if( (numArgs & 1) != 0 ) {
        printWrongArgCount( msg );
      }
     
      for( int i = 0; i < numArgs; ) {
        id  = decodeMessageArg( msg, c, i++ );
        val  = decodeMessageArg( msg, c, i++ );
        globals.put( id, val );
      }
     
      return id;
  //    return numArgs < 2 ? null : msgArgs[ numArgs - 2 ];
    }
  } // class CmdGlobal

  /**
   *  Command: /set String <objectID>, [ String <propertyName1>, Object <propertyValue1> ... ]
   */
  private class CmdSet
  extends BasicCmd
  {
    protected CmdSet()
    {
      super( "/set" );
    }

    /**
     *  Note that each message argument is parsed strictly after another
     *  so that side effect statements in later arguments may assume that earlier properties
     *  have already been set.
     *
     *  @param  msg    the /set message
     *  @param  c    the client
     *  @return      the last value
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]    msgArgs    = decodeMessageArgs( msg, addr );
      final Object      id;
      final Object      o;
      int          numArgs    = msg.getArgCount(); // msgArgs.length;
      Object        result    = null;
      String        propName;
      Object        propValue;
 
      if( numArgs >= 1 ) {
        if( (numArgs & 1) == 0 ) {
          printWrongArgCount( msg );
          numArgs--;  // avoid array index exception
        }
 
        id  = decodeMessageArg( msg, c, 0 );
        o   = c.getObject( id );
        if( o != null ) {
          for( int i = 1; i < numArgs; ) {
            propName    = decodeMessageArg( msg, c, i++ ).toString();
            propValue  = decodeMessageArg( msg, c, i++ );
            try {
              result  = setProperty( msg, propName, propValue, o );
            }
            catch( LinkageError e ) {
              printException( e, msg );
            }
            catch( SecurityException e ) {
              printException( e, msg );
            }   
            catch( NoSuchMethodException e ) {
              printException( e, msg );
            }
            catch( IllegalAccessException e ) {
              printException( e, msg );
            }
            catch( IllegalArgumentException e ) {
              printException( e, msg );
            }
            catch( InvocationTargetException e ) {
              printException( e.getTargetException(), msg );
            }
            catch( ClassCastException e ) {
              printException( e, msg );
            }
            catch( NoSuchFieldException e ) {
              printException( e, msg );
            }
          }
        } else {
          printNotFound( msg, id );
        }
      } else {
        printArgMismatch( msg );
      }
      return result;
    }
  } // class CmdSet

  /**
   *  Command: /get String <objectID>, [ String <propertyName1>, ... ]
   */
  private class CmdGet
  extends BasicCmd
  {
    protected CmdGet()
    {
      super( "/get" );
    }

    /**
     *  The message args are processed strictly after another.
     *
     *  @param  msg    the /get message
     *  @param  c    the client
     *  @return      the last value
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]    msgArgs    = decodeMessageArgs( msg, addr );
      final Object      o;
      final Object      id;
      final Object[]    replyArgs;
      int          numArgs    = msg.getArgCount(); // msgArgs.length;
      Object        result    = null;
      String        propName;
 
      if( numArgs >= 1 ) {
        id  = decodeMessageArg( msg, c, 0 );
        o   = c.getObject( id );
        if( o != null ) {
          replyArgs    = new Object[ (numArgs << 1) - 1 ];
          replyArgs[ 0 = id;
          try {
            for( int replyOff = 1, propOff = 1; propOff < numArgs; propOff++ ) {
              propName  = decodeMessageArg( msg, c, propOff ).toString();
              replyArgs[ replyOff++ = propName;
              result          = getProperty( propName, o );
              replyArgs[ replyOff++ = result;
            }
            c.reply( new OSCMessage( "/set", replyArgs ));
          }
          catch( LinkageError e ) {
            printException( e, msg );
          }
          catch( SecurityException e ) {
            printException( e, msg );
          }   
          catch( NoSuchMethodException e ) {
            printException( e, msg );
          }
          catch( IllegalAccessException e ) {
            printException( e, msg );
          }
          catch( IllegalArgumentException e ) {
            printException( e, msg );
          }
          catch( InvocationTargetException e ) {
            printException( e.getTargetException(), msg );
          }
          catch( ClassCastException e ) {
            printException( e, msg );
          }
         
        } else {
          printNotFound( msg, id );
        }
      } else {
        printArgMismatch( msg );
      }
      return result;
    }
  } // class CmdGet

  /**
   *  Command: /method, String <objectID>, String <methodName>, [ Object <arg1> ... ]
   */
  private class CmdMethod
  extends BasicCmd
  {
    protected CmdMethod()
    {
      super( "/method" );
    }

    /**
     *  @param  msg    the /method message
     *  @param  c    the client
     *  @return      the return value of the method
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[] msgArgs = decodeMessageArgs( msg, addr );
      final Object[]  msgArgs;
      final int    numArgs = msg.getArgCount() - 2;
      final Object    id;
      final Object    o;
      final Object    name;
 
      if( numArgs >= 0 ) {
        id   = decodeMessageArg( msg, c, 0 );
        o  = c.getObject( id )// de-reference
        if( o != null ) {
          name = decodeMessageArg( msg, c, 1 );
          if( name != null ) {
            msgArgs = new Object[ numArgs ];
            for( int i = 0, j = 2; i < numArgs; i++, j++ ) {
              msgArgs[ i ] = decodeMessageArg( msg, c, j );
            }
            return invokeMethod( msg, o, name.toString(), msgArgs );
          } else {
            printFailed( msg, "Method name is null" );       
          }
        } else {
          printNotFound( msg, id );
        }
      } else {
        printArgMismatch( msg );
      }
     
      return null;
    }
  } // class CmdMethod

  /**
   *  Command: /methodr, Object <object>, String <methodName>, [ Object <arg1> ... ]
   */
  private class CmdMethodR
  extends BasicCmd
  {
    protected CmdMethodR()
    {
      super( "/methodr" );
    }

    /**
     *  @param  msg    the /methodr message
     *  @param  c    the client
     *  @return      the return value of the method
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[] msgArgs = decodeMessageArgs( msg, addr );
      final Object[]  msgArgs;
      final int    numArgs = msg.getArgCount() - 2;
      final Object    o;
      final Object    name;
 
      if( numArgs >= 0 ) {
        o = decodeMessageArg( msg, c, 0 );
        if( o != null ) {
          name = decodeMessageArg( msg, c, 1 );
          if( name != null ) {
            msgArgs = new Object[ numArgs ];
            for( int i = 0, j = 2; i < numArgs; i++, j++ ) {
              msgArgs[ i ] = decodeMessageArg( msg, c, j );
            }
            return invokeMethod( msg, o, name.toString(), msgArgs );
          } else {
            printFailed( msg, "Method name is null" );       
          }
        } else {
          printFailed( msg, "Cannot operate on null result" );
        }
      } else {
        printArgMismatch( msg );
      }
     
      return null;
    }
  } // class CmdMethodR

  /**
   *  Command: /field, String <objectID>, String <fieldName>
   */
  private class CmdField
  extends BasicCmd
  {
    protected CmdField()
    {
      super( "/field" );
    }

    /**
     *  @param  msg    the /field message
     *  @param  c    the client
     *  @return      the value of the field
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]  msgArgs = decodeMessageArgs( msg, addr );
      final Object    o;
      final Object    id;
      final Object    name;
 
      if( msg.getArgCount() >= 2 ) {
        id  = decodeMessageArg( msg, c, 0 );
        o  = c.getObject( id )// de-reference
        if( o != null ) {
          name = decodeMessageArg( msg, c, 1 );
          if( name != null ) {
            return invokeField( msg, o, name.toString() );
          } else {
            printFailed( msg, "Field name is null" );       
          }
        } else {
          printNotFound( msg, id );
        }
      } else {
        printArgMismatch( msg );
      }
     
      return null;
    }
  } // class CmdField
 
  /**
   *  Command: /fieldr, Object <object>, String <fieldName>
   */
  private class CmdFieldR
  extends BasicCmd
  {
    protected CmdFieldR()
    {
      super( "/fieldr" );
    }

    /**
     *  @param  msg    the /fieldr message
     *  @param  c    the client
     *  @return      the value of the field
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]  msgArgs = decodeMessageArgs( msg, addr );
      final Object    o;
      final Object    name;
 
      if( msg.getArgCount() >= 2 ) {
        o  = decodeMessageArg( msg, c, 0 );
        if( o != null ) {
          name = decodeMessageArg( msg, c, 1 );
          if( name != null ) {
            return invokeField( msg, o, name.toString() );
          } else {
            printFailed( msg, "Field name is null" );       
          }
        } else {
          printFailed( msg, "Cannot operate on null result" );
        }
      } else {
        printArgMismatch( msg );
      }
     
      return null;
    }
  } // class CmdFieldR

  /**
   *  Command: /free, [ String <objectID1>, ... ]
   */
  private class CmdFree
  extends BasicCmd
  {
    protected CmdFree()
    {
      super( "/free" );
    }

    /**
     *  Note that each argument is strictly parsed after another which
     *  means that later statements cannot access earlier (now freed)
     *  object bindings. That is:
     *  <pre>
     *  [ "/free", "schoko", [ "/method", "schoko", "getAnotherObjectID" ]]
     *  </pre>
     *  will fail.
     *
     *  @param  msg    the /free message
     *  @param  c    the client
     *  @return      the value of the last object in the list
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      final int    numArgs  = msg.getArgCount(); // msgArgs.length;
      Object      id;
      Object      result  = null;
     
      for( int i = 0; i < numArgs; i++ ) {
        id    = decodeMessageArg( msg, c, i );
        result  = c.locals.remove( id );
        if( result == null ) {
          result  = globals.remove( id );
        }
        if( result == null ) {
          printNotFound( msg, id );
        }
      }
      return result;
    }
  } // class CmdFree

  /**
   *  Command: /array, [ Object <object1>, ... ]
   */
  private class CmdArray
  extends BasicCmd
  {
    protected CmdArray()
    {
      super( "/array" );
    }

    /**
     *  @param  msg    the /array message
     *  @param  c    the client
     *  @return      the object array
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      return decodeMessageArgs( msg, c );
    }
  } // class CmdArray

  /**
   *  Command: /query, [ String <returnID1>, Object <anObject1> ... ]
   */
  private class CmdQuery
  extends BasicCmd
  {
    protected CmdQuery()
    {
      super( "/query" );
    }

    /**
     *  @param  msg    the /query message
     *  @param  c    the client
     *  @return      the last object's return value
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      final Object[]  msgArgs    = decodeMessageArgs( msg, c );
      final int    numArgs    = msgArgs.length;
 
      if( (numArgs & 1) != 0 ) {
        printWrongArgCount( msg );
      }
 
      c.reply( new OSCMessage( "/info", msgArgs ));
     
      return numArgs == 0 ? null : msgArgs[ numArgs - 1 ];
    }
  } // class CmdQuery
 
  /**
   *  Command: /print, [ String <objectID1>, ... ]
   */
  private class CmdPrint
  extends BasicCmd
  {
    protected CmdPrint()
    {
      super( "/print" );
    }

    /**
     *  Note that arguments are processed strictly after another.
     *
     *  @param  msg    the /print message
     *  @param  c    the client
     *  @return      the last object's value
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
  //    final Object[]  msgArgs  = decodeMessageArgs( msg, addr );
      final int    numArgs  = msg.getArgCount(); // msgArgs.length;
      Object      id;
      Object      o    = null;
     
      for( int i = 0; i < numArgs; i++ ) {
        id  = decodeMessageArg( msg, c, i );
        o  = c.getObject( id );
        System.out.println( id + " : " + o );
      }
      return o;
    }
  } // class CmdPrint
 
  /**
   *  Command: /dumpOSC, int <incomingMode> [, int <outgoingMode> ]
   */
  private class CmdDumpOSC
  extends BasicCmd
  {
    protected CmdDumpOSC()
    {
      super( "/dumpOSC" );
    }

    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      final int numArgs  = msg.getArgCount();
   
      if( (numArgs >= 1) && (numArgs <= 2) && (msg.getArg( 0 ) instanceof Number) ) {
        serv.dumpIncomingOSC( ((Number) msg.getArg( 0 )).intValue(), System.out );
        if( numArgs == 2 ) {
          if( msg.getArg( 1 ) instanceof Number ) {
            serv.dumpOutgoingOSC( ((Number) msg.getArg( 1 )).intValue(), System.out );
          } else {
            printArgMismatch( msg );
          }
        }
      } else {
        printArgMismatch( msg );
      }
     
      return null;
    }
  } // class CmdDumpOSC

  /**
   *  Command: /classes, String <cmd>, [ String <classPathURL1>, ... ]
   *
   *  where cmd is one of "add", "remove", "update"
   */
  private class CmdClasses
  extends BasicCmd
  {
    protected CmdClasses()
    {
      super( "/classes" );
    }

    /**
     *  @param  msg    the /classes message
     *  @param  c    the client
     *  @return      the last path
     */
    public Object processMessage( OSCMessage msg, SwingClient c )
    throws IOException
    {
      final int    numArgs  = msg.getArgCount();
      final int    numURLs  = numArgs - 1;
      final URL[]    paths;
      final String  cmd;
      String      path  = null;
     
      if( numArgs > 0 ) {
        cmd  = decodeMessageArg( msg, c, 0 ).toString();
        paths = new URL[ numURLs ];
        for( int i = 0; i < numURLs; i++ ) {
          path    = decodeMessageArg( msg, c, i + 1 ).toString();
          paths[ i = new URL( path );
        }
        if( cmd.equals( "add" )) {
          classLoaderMgr.addURLs( paths );
          return path;
        } else if( cmd.equals( "remove" )) {
          classLoaderMgr.removeURLs( paths );
          return path;
        } else if( cmd.equals( "update" )) {
          classLoaderMgr.removeURLs( paths );
          classLoaderMgr.addURLs( paths );
          return path;
        } else {
          printArgMismatch( msg );
        }
      } else {
        printArgMismatch( msg );
      }
      return null;
    }
  } // class CmdClasses
} // class SwingOSC
TOP

Related Classes of de.sciss.swingosc.SwingOSC$CmdMethod

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.