summaryrefslogtreecommitdiffstats
path: root/ridljar/com/sun/star/lib
diff options
context:
space:
mode:
Diffstat (limited to 'ridljar/com/sun/star/lib')
-rw-r--r--ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java213
-rw-r--r--ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java123
-rw-r--r--ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java120
-rw-r--r--ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java98
-rw-r--r--ridljar/com/sun/star/lib/connections/socket/SocketConnection.java235
-rw-r--r--ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java202
-rw-r--r--ridljar/com/sun/star/lib/connections/socket/socketConnector.java174
-rw-r--r--ridljar/com/sun/star/lib/uno/Proxy.java34
-rw-r--r--ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java117
-rw-r--r--ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java145
-rw-r--r--ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java80
-rw-r--r--ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java218
-rw-r--r--ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java94
-rw-r--r--ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java117
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java43
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java198
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java35
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java76
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java89
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java700
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/java/java_environment.java312
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java93
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java40
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java115
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java124
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java87
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/Job.java163
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java373
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/Message.java189
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java94
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java109
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java74
-rw-r--r--ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java66
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/ComponentBase.java136
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/Factory.java291
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java864
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java155
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/PropertySet.java1103
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java1111
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/UnoUrl.java401
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java94
-rw-r--r--ridljar/com/sun/star/lib/uno/helper/WeakBase.java101
-rw-r--r--ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java114
-rw-r--r--ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java355
-rw-r--r--ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java65
-rw-r--r--ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java476
-rw-r--r--ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java48
-rw-r--r--ridljar/com/sun/star/lib/uno/protocols/urp/urp.java760
-rw-r--r--ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java78
-rw-r--r--ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java54
-rw-r--r--ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java131
-rw-r--r--ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java819
-rw-r--r--ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java86
-rw-r--r--ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java32
-rw-r--r--ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java96
-rw-r--r--ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java89
-rw-r--r--ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java105
-rw-r--r--ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java76
-rw-r--r--ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java117
-rw-r--r--ridljar/com/sun/star/lib/util/DisposeListener.java34
-rw-r--r--ridljar/com/sun/star/lib/util/DisposeNotifier.java44
-rw-r--r--ridljar/com/sun/star/lib/util/NativeLibraryLoader.java141
-rw-r--r--ridljar/com/sun/star/lib/util/StringHelper.java46
-rw-r--r--ridljar/com/sun/star/lib/util/UrlToFileMapper.java94
-rw-r--r--ridljar/com/sun/star/lib/util/WeakMap.java319
65 files changed, 13385 insertions, 0 deletions
diff --git a/ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java b/ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java
new file mode 100644
index 000000000..b3a686f17
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java
@@ -0,0 +1,213 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.connections.pipe;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import com.sun.star.connection.XConnection;
+import com.sun.star.connection.XConnectionBroadcaster;
+import com.sun.star.io.XStreamListener;
+import com.sun.star.lib.util.NativeLibraryLoader;
+
+/**
+ * The PipeConnection implements the <code>XConnection</code> interface
+ * and is uses by the <code>PipeConnector</code> and the <code>PipeAcceptor</code>.
+ * This class is not part of the provided <code>api</code>.
+ *
+ * The native implementation is in jurt/source/pipe/com_sun_star_lib_connections_pipe_PipeConnection.c
+ *
+ * @see com.sun.star.lib.connections.pipe.pipeAcceptor
+ * @see com.sun.star.lib.connections.pipe.pipeConnector
+ * @see com.sun.star.connection.XConnection
+ * @since UDK1.0
+ */
+public class PipeConnection implements XConnection, XConnectionBroadcaster {
+ /**
+ * When set to true, enables various debugging output.
+ */
+ public static final boolean DEBUG = false;
+
+ static {
+ // load shared library for JNI code
+ NativeLibraryLoader.loadLibrary(PipeConnection.class.getClassLoader(), "jpipe");
+ }
+
+ protected String _aDescription;
+ protected long _nPipeHandle;
+ protected ArrayList<XStreamListener> _aListeners;
+ protected boolean _bFirstRead;
+
+ /**
+ * Constructs a new <code>PipeConnection</code>.
+ *
+ * @param description the description of the connection.
+ */
+ public PipeConnection(String description)
+ throws IOException
+ {
+ if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated " + description );
+
+ _aListeners = new ArrayList<XStreamListener>();
+ _bFirstRead = true;
+
+ // get pipe name from pipe descriptor
+ String aPipeName ;
+ StringTokenizer aTokenizer = new StringTokenizer( description, "," );
+ if ( aTokenizer.hasMoreTokens() ) {
+ String aConnType = aTokenizer.nextToken();
+ if ( !aConnType.equals( "pipe" ) )
+ throw new RuntimeException( "invalid pipe descriptor: does not start with 'pipe,'" );
+
+ String aPipeNameParam = aTokenizer.nextToken();
+ if ( !aPipeNameParam.substring( 0, 5 ).equals( "name=" ) )
+ throw new RuntimeException( "invalid pipe descriptor: no 'name=' parameter found" );
+ aPipeName = aPipeNameParam.substring( 5 );
+ }
+ else
+ throw new RuntimeException( "invalid or empty pipe descriptor" );
+
+ // create the pipe
+ try {
+ createJNI( aPipeName );
+ } catch ( java.lang.Exception ex1 ) {
+ IOException ex2 = new IOException();
+ ex2.initCause(ex1);
+ throw ex2;
+ }
+ }
+
+ public void addStreamListener(XStreamListener aListener ) throws com.sun.star.uno.RuntimeException {
+ _aListeners.add(aListener);
+ }
+
+ public void removeStreamListener(XStreamListener aListener ) throws com.sun.star.uno.RuntimeException {
+ _aListeners.remove(aListener);
+ }
+
+ private void notifyListeners_open() {
+ for (XStreamListener xStreamListener : _aListeners) {
+ xStreamListener.started();
+ }
+ }
+
+ private void notifyListeners_close() {
+ for (XStreamListener xStreamListener : _aListeners) {
+ xStreamListener.closed();
+ }
+ }
+
+ private void notifyListeners_error(com.sun.star.uno.Exception exception) {
+ for (XStreamListener xStreamListener : _aListeners) {
+ xStreamListener.error(exception);
+ }
+ }
+
+ // JNI implementation to create the pipe
+ private native int createJNI( String name )
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException;
+
+ // JNI implementation to read from the pipe
+ private native int readJNI(/*OUT*/byte[][] bytes, int nBytesToRead)
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException;
+
+ // JNI implementation to write to the pipe
+ private native void writeJNI(byte aData[])
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException;
+
+ // JNI implementation to flush the pipe
+ private native void flushJNI()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException;
+
+ // JNI implementation to close the pipe
+ private native void closeJNI()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException;
+
+ /**
+ * Read the required number of bytes.
+ *
+ * @param bytes the outparameter, where the bytes have to be placed.
+ * @param nBytesToRead the number of bytes to read.
+ * @return the number of bytes read.
+ *
+ * @see com.sun.star.connection.XConnection#read
+ */
+ public int read(/*OUT*/byte[][] bytes, int nBytesToRead)
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ if(_bFirstRead) {
+ _bFirstRead = false;
+
+ notifyListeners_open();
+ }
+
+ return readJNI( bytes, nBytesToRead );
+ }
+
+ /**
+ * Write bytes.
+ *
+ * @param aData the bytes to write.
+ * @see com.sun.star.connection.XConnection#write
+ */
+ public void write(byte aData[])
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ writeJNI( aData );
+ }
+
+ /**
+ * Flushes the buffer.
+ *
+ * @see com.sun.star.connection.XConnection#flush
+ */
+ public void flush()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ flushJNI();
+ }
+
+ /**
+ * Closes the connection.
+ *
+ * @see com.sun.star.connection.XConnection#close
+ */
+ public void close()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ if (DEBUG) System.out.print( "PipeConnection::close() " );
+ closeJNI();
+ notifyListeners_close();
+ if (DEBUG) System.out.println( "done" );
+ }
+
+ /**
+ * Gives a description of the connection.
+ *
+ * @return the description.
+ * @see com.sun.star.connection.XConnection#getDescription
+ */
+ public String getDescription() throws com.sun.star.uno.RuntimeException {
+ return _aDescription;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java b/ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java
new file mode 100644
index 000000000..983eb3265
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java
@@ -0,0 +1,123 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.connections.pipe;
+
+import com.sun.star.comp.loader.FactoryHelper;
+import com.sun.star.connection.AlreadyAcceptingException;
+import com.sun.star.connection.ConnectionSetupException;
+import com.sun.star.connection.XAcceptor;
+import com.sun.star.connection.XConnection;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+import com.sun.star.registry.XRegistryKey;
+
+/**
+ * A component that implements the <code>XAcceptor</code> interface.
+ *
+ * <p>The <code>pipeAcceptor</code> is a specialized component that uses TCP
+ * pipes for communication. The <code>pipeAcceptor</code> is generally used
+ * by the <code>com.sun.star.connection.Acceptor</code> service.</p>
+ *
+ * @see com.sun.star.connection.XAcceptor
+ * @see com.sun.star.connection.XConnection
+ * @see com.sun.star.connection.XConnector
+ * @see com.sun.star.comp.loader.JavaLoader
+ *
+ * @since UDK 1.0
+ */
+public final class pipeAcceptor implements XAcceptor {
+ /**
+ * The name of the service.
+ *
+ * <p>The <code>JavaLoader</code> accesses this through reflection.</p>
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static final String __serviceName
+ = "com.sun.star.connection.pipeAcceptor";
+
+ /**
+ * Returns a factory for creating the service.
+ *
+ * <p>This method is called by the <code>JavaLoader</code>.</p>
+ *
+ * @param implName the name of the implementation for which a service is
+ * requested.
+ * @param multiFactory the service manager to be used (if needed).
+ * @param regKey the registry key.
+ * @return an <code>XSingleServiceFactory</code> for creating the component.
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleServiceFactory __getServiceFactory(
+ String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey)
+ {
+ return implName.equals(pipeAcceptor.class.getName())
+ ? FactoryHelper.getServiceFactory(
+ pipeAcceptor.class, __serviceName, multiFactory, regKey)
+ : null;
+ }
+
+ /**
+ * Accepts a connection request via the described pipe.
+ *
+ * <p>This call blocks until a connection has been established.</p>
+ *
+ * <p>The connection description has the following format:
+ * <code><var>type</var></code><!--
+ * -->*(<code><var>key</var>=<var>value</var></code>),
+ * where <code><var>type</var></code> should be <code>pipe</code>
+ * (ignoring case). Supported keys (ignoring case) currently are</p>
+ * <dl>
+ * <dt><code>host</code>
+ * <dd>The name or address of the accepting interface (defaults to
+ * <code>0</code>, meaning any interface).
+ * <dt><code>port</code>
+ * <dd>The TCP port number to accept on (defaults to <code>6001</code>).
+ * <dt><code>backlog</code>
+ * <dd>The maximum length of the acceptor's queue (defaults to
+ * <code>50</code>).
+ * <dt><code>tcpnodelay</code>
+ * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's
+ * algorithm on the resulting connection.
+ * </dl>
+ *
+ * @param connectionDescription the description of the connection.
+ * @return an <code>XConnection</code> to the client.
+ *
+ * @see com.sun.star.connection.XConnection
+ * @see com.sun.star.connection.XConnector
+ */
+ public XConnection accept(String connectionDescription) throws
+ AlreadyAcceptingException, ConnectionSetupException,
+ com.sun.star.lang.IllegalArgumentException
+ {
+ throw new java.lang.NoSuchMethodError( "pipeAcceptor not fully implemented yet" );
+ }
+
+ /**
+ *
+ * @see com.sun.star.connection.XAcceptor#stopAccepting
+ */
+ public void stopAccepting() {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java b/ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java
new file mode 100644
index 000000000..192d35007
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java
@@ -0,0 +1,120 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.connections.pipe;
+
+import com.sun.star.comp.loader.FactoryHelper;
+import com.sun.star.connection.ConnectionSetupException;
+import com.sun.star.connection.NoConnectException;
+import com.sun.star.connection.XConnection;
+import com.sun.star.connection.XConnector;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+import com.sun.star.registry.XRegistryKey;
+
+/**
+ * A component that implements the <code>XConnector</code> interface.
+ *
+ * <p>The <code>pipeConnector</code> is a specialized component that uses TCP
+ * pipes for communication. The <code>pipeConnector</code> is generally
+ * used by the <code>com.sun.star.connection.Connector</code> service.</p>
+ *
+ * @see com.sun.star.connection.XAcceptor
+ * @see com.sun.star.connection.XConnection
+ * @see com.sun.star.connection.XConnector
+ * @see com.sun.star.comp.loader.JavaLoader
+ *
+ * @since UDK 1.0
+ */
+public final class pipeConnector implements XConnector {
+ /**
+ * The name of the service.
+ *
+ * <p>The <code>JavaLoader</code> accesses this through reflection.</p>
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static final String __serviceName = "com.sun.star.connection.pipeConnector";
+
+ /**
+ * Returns a factory for creating the service.
+ *
+ * <p>This method is called by the <code>JavaLoader</code>.</p>
+ *
+ * @param implName the name of the implementation for which a service is
+ * requested.
+ * @param multiFactory the service manager to be used (if needed).
+ * @param regKey the registry key.
+ * @return an <code>XSingleServiceFactory</code> for creating the component.
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleServiceFactory __getServiceFactory(
+ String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey)
+ {
+ return implName.equals(pipeConnector.class.getName())
+ ? FactoryHelper.getServiceFactory(pipeConnector.class,
+ __serviceName, multiFactory,
+ regKey)
+ : null;
+ }
+
+ /**
+ * Connects via the described pipe to a waiting server.
+ *
+ * <p>The connection description has the following format:
+ * <code><var>type</var></code><!--
+ * -->*(<code><var>key</var>=<var>value</var></code>),
+ * where <code><var>type</var></code> should be <code>pipe</code>
+ * (ignoring case). Supported keys (ignoring case) currently are</p>
+ * <dl>
+ * <dt><code>host</code>
+ * <dd>The name or address of the server. Must be present.
+ * <dt><code>port</code>
+ * <dd>The TCP port number of the server (defaults to <code>6001</code>).
+ * <dt><code>tcpnodelay</code>
+ * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's
+ * algorithm on the resulting connection.
+ * </dl>
+ *
+ * @param connectionDescription the description of the connection.
+ * @return an <code>XConnection</code> to the server.
+ *
+ * @see com.sun.star.connection.XAcceptor
+ * @see com.sun.star.connection.XConnection
+ */
+ public synchronized XConnection connect(String connectionDescription)
+ throws NoConnectException, ConnectionSetupException
+ {
+ if (bConnected)
+ throw new ConnectionSetupException("already connected");
+
+ try {
+ XConnection xConn = new PipeConnection( connectionDescription );
+ bConnected = true;
+ return xConn;
+ } catch ( java.io.IOException e ) {
+ throw new NoConnectException(e);
+ }
+ }
+
+ private boolean bConnected = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java b/ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java
new file mode 100644
index 000000000..5a88d41ba
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java
@@ -0,0 +1,98 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.connections.socket;
+
+/**
+ * Helper class for <code>socketAcceptor</code> and <code>socketConnector</code>.
+ *
+ * <p>FIXME: Once those classes have been moved from <code>jurt</code> to
+ * <code>javaunohelper</code>, they should use
+ * <code>com.sun.star.lib.uno.helper.UnoUrl</code> either instead of this class
+ * or underneath this class.</p>
+ */
+final class ConnectionDescriptor {
+ public ConnectionDescriptor(String description)
+ throws com.sun.star.lang.IllegalArgumentException {
+ for (int i = description.indexOf(','); i >= 0;) {
+ int j = description.indexOf(',', i + 1);
+ int k = j < 0 ? description.length() : j;
+ int l = description.indexOf('=', i + 1);
+ if (l < 0 || l >= k) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "parameter lacks '='");
+ }
+ String key = description.substring(i + 1, l);
+ String value = description.substring(l + 1, k);
+ if (key.equalsIgnoreCase("host")) {
+ host = value;
+ } else if (key.equalsIgnoreCase("port")) {
+ try {
+ port = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new com.sun.star.lang.IllegalArgumentException(e);
+ }
+ if (port < 0 || port > 65535) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "port parameter must have value between 0 and 65535,"
+ + " inclusive");
+ }
+ } else if (key.equalsIgnoreCase("backlog")) {
+ try {
+ backlog = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new com.sun.star.lang.IllegalArgumentException(e);
+ }
+ } else if (key.equalsIgnoreCase("tcpnodelay")) {
+ if (value.equals("0")) {
+ tcpNoDelay = Boolean.FALSE;
+ } else if (value.equals("1")) {
+ tcpNoDelay = Boolean.TRUE;
+ } else {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "tcpnodelay parameter must have 0/1 value");
+ }
+ }
+ i = j;
+ }
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public int getBacklog() {
+ return backlog;
+ }
+
+ public Boolean getTcpNoDelay() {
+ return tcpNoDelay;
+ }
+
+ private String host = null;
+ private int port = 6001;
+ private int backlog = 50;
+ private Boolean tcpNoDelay = null;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/connections/socket/SocketConnection.java b/ridljar/com/sun/star/lib/connections/socket/SocketConnection.java
new file mode 100644
index 000000000..a906496f2
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/socket/SocketConnection.java
@@ -0,0 +1,235 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.connections.socket;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.net.Socket;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.sun.star.connection.XConnection;
+import com.sun.star.connection.XConnectionBroadcaster;
+import com.sun.star.io.XStreamListener;
+
+/**
+ * The SocketConnection implements the <code>XConnection</code> interface
+ * and is uses by the <code>SocketConnector</code> and the <code>SocketAcceptor</code>.
+ *
+ * <p>This class is not part of the provided <code>api</code>.</p>
+ *
+ * @see com.sun.star.lib.connections.socket.socketAcceptor
+ * @see com.sun.star.lib.connections.socket.socketConnector
+ * @see com.sun.star.connection.XConnection
+ * @since UDK1.0
+ */
+public class SocketConnection implements XConnection, XConnectionBroadcaster {
+ /**
+ * When set to true, enables various debugging output.
+ */
+ public static final boolean DEBUG = false;
+
+ protected String _description;
+ protected Socket _socket;
+ protected InputStream _inputStream;
+ protected OutputStream _outputStream;
+ protected ArrayList<XStreamListener> _listeners;
+ protected boolean _firstRead;
+
+ /**
+ * Constructs a new <code>SocketConnection</code>.
+ *
+ * @param description the description of the connection.
+ * @param socket the socket of the connection.
+ */
+ public SocketConnection(String description, Socket socket) throws IOException {
+ if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated " + description + " " + socket);
+
+ _description = description
+ + ",localHost=" + socket.getLocalAddress().getHostName()
+ + ",localPort=" + socket.getLocalPort()
+ + ",peerHost=" + socket.getInetAddress().getHostName()
+ + ",peerPort=" + socket.getPort();
+
+ _socket = socket;
+ _inputStream = new BufferedInputStream(socket.getInputStream());
+ _outputStream = new BufferedOutputStream(socket.getOutputStream());
+
+ _listeners = new ArrayList<XStreamListener>();
+ _firstRead = true;
+ }
+
+ public void addStreamListener(XStreamListener aListener )
+ throws com.sun.star.uno.RuntimeException {
+ _listeners.add(aListener);
+ }
+
+ public void removeStreamListener(XStreamListener aListener )
+ throws com.sun.star.uno.RuntimeException {
+ _listeners.remove(aListener);
+ }
+
+ private void notifyListeners_open() {
+ for (XStreamListener xStreamListener : _listeners) {
+ xStreamListener.started();
+ }
+ }
+
+ private void notifyListeners_close() {
+ for (XStreamListener xStreamListener : _listeners) {
+ xStreamListener.closed();
+ }
+ }
+
+ private void notifyListeners_error(com.sun.star.uno.Exception exception) {
+ for (XStreamListener xStreamListener : _listeners) {
+ xStreamListener.error(exception);
+ }
+ }
+
+ /**
+ * Read the required number of bytes.
+ *
+ * @param bytes the outparameter, where the bytes have to be placed.
+ * @param nBytesToRead the number of bytes to read.
+ * @return the number of bytes read.
+ *
+ * @see com.sun.star.connection.XConnection#read
+ */
+ public int read(/*OUT*/byte[][] bytes, int nBytesToRead)
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException {
+ if(_firstRead) {
+ _firstRead = false;
+
+ notifyListeners_open();
+ }
+
+ String errMessage = null;
+
+ int read_bytes = 0;
+ bytes[0] = new byte[nBytesToRead];
+
+ try {
+ int count ;
+
+ do {
+ count = _inputStream.read(bytes[0], read_bytes, nBytesToRead - read_bytes);
+ if(count == -1)
+ errMessage = "EOF reached - " + getDescription();
+
+ read_bytes += count;
+ }
+ while(read_bytes >= 0 && read_bytes < nBytesToRead && count >= 0);
+ } catch(IOException ioException) {
+ if(DEBUG) {
+ System.err.println("##### " + getClass().getName() + ".read - exception occurred:" + ioException);
+ ioException.printStackTrace();
+ }
+
+ errMessage = ioException.toString();
+ }
+
+ if(errMessage != null) {
+ com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(errMessage);
+ notifyListeners_error(unoIOException);
+
+ throw unoIOException;
+ }
+
+ if (DEBUG) System.err.println("##### " + getClass().getName() + " - read byte:" + read_bytes + " " + Arrays.toString(bytes[0]));
+
+ return read_bytes;
+ }
+
+ /**
+ * Write bytes.
+ *
+ * @param aData the bytes to write.
+ * @see com.sun.star.connection.XConnection#write
+ */
+ public void write(byte aData[]) throws com.sun.star.io.IOException,
+ com.sun.star.uno.RuntimeException {
+ try {
+ _outputStream.write(aData);
+ } catch(IOException ioException) {
+ com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(ioException);
+ notifyListeners_error(unoIOException);
+
+ throw unoIOException;
+ }
+
+ if (DEBUG) System.err.println("##### " + getClass().getName() + " - written bytes:" + Arrays.toString(aData) + " " + aData.length);
+ }
+
+ /**
+ * Flushes the buffer.
+ *
+ * @see com.sun.star.connection.XConnection#flush
+ */
+ public void flush() throws com.sun.star.io.IOException,
+ com.sun.star.uno.RuntimeException {
+ try {
+ _outputStream.flush();
+ } catch(IOException ioException) {
+ com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(ioException);
+ notifyListeners_error(unoIOException);
+
+ throw unoIOException;
+ }
+ }
+
+ /**
+ * Closes the connection.
+ *
+ * @see com.sun.star.connection.XConnection#close
+ */
+ public void close() throws com.sun.star.io.IOException,
+ com.sun.star.uno.RuntimeException {
+ try {
+ _socket.close();
+ } catch(IOException ioException) {
+ com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(ioException);
+ notifyListeners_error(unoIOException);
+
+ throw unoIOException;
+ }
+ if (DEBUG) System.err.println("##### " + getClass().getName() + " - socket closed");
+
+ notifyListeners_close();
+ }
+
+ /**
+ * Gives a description of the connection.
+ *
+ * @return the description.
+ * @see com.sun.star.connection.XConnection#getDescription
+ */
+ public String getDescription() throws com.sun.star.uno.RuntimeException {
+ return _description;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java b/ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java
new file mode 100644
index 000000000..4000a1d0a
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java
@@ -0,0 +1,202 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.connections.socket;
+
+import com.sun.star.comp.loader.FactoryHelper;
+import com.sun.star.connection.AlreadyAcceptingException;
+import com.sun.star.connection.ConnectionSetupException;
+import com.sun.star.connection.XAcceptor;
+import com.sun.star.connection.XConnection;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+import com.sun.star.registry.XRegistryKey;
+
+import java.io.IOException;
+import java.net.*;
+
+/**
+ * A component that implements the <code>XAcceptor</code> interface.
+ *
+ * <p>The <code>socketAcceptor</code> is a specialized component that uses TCP
+ * sockets for communication. The <code>socketAcceptor</code> is generally used
+ * by the <code>com.sun.star.connection.Acceptor</code> service.</p>
+ *
+ * @see com.sun.star.connection.XAcceptor
+ * @see com.sun.star.connection.XConnection2
+ * @see com.sun.star.connection.XConnector
+ * @see com.sun.star.comp.loader.JavaLoader
+ *
+ * @since UDK 1.0
+ */
+public final class socketAcceptor implements XAcceptor {
+ /**
+ * The name of the service.
+ *
+ * <p>The <code>JavaLoader</code> accesses this through reflection.</p>
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static final String __serviceName
+ = "com.sun.star.connection.socketAcceptor";
+
+ /**
+ * Returns a factory for creating the service.
+ *
+ * <p>This method is called by the <code>JavaLoader</code>.</p>
+ *
+ * @param implName the name of the implementation for which a service is
+ * requested.
+ * @param multiFactory the service manager to be used (if needed).
+ * @param regKey the registry key.
+ * @return an <code>XSingleServiceFactory</code> for creating the component.
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleServiceFactory __getServiceFactory(
+ String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey)
+ {
+ return implName.equals(socketAcceptor.class.getName())
+ ? FactoryHelper.getServiceFactory(socketAcceptor.class,
+ __serviceName, multiFactory,
+ regKey)
+ : null;
+ }
+
+ /**
+ * Accepts a connection request via the described socket.
+ *
+ * <p>This call blocks until a connection has been established.</p>
+ *
+ * <p>The connection description has the following format:
+ * <code><var>type</var></code><!--
+ * -->*(<code><var>key</var>=<var>value</var></code>),
+ * where <code><var>type</var></code> should be <code>socket</code>
+ * (ignoring case). Supported keys (ignoring case) currently are</p>
+ * <dl>
+ * <dt><code>host</code>
+ * <dd>The name or address of the accepting interface (defaults to
+ * <code>0</code>, meaning any interface).
+ * <dt><code>port</code>
+ * <dd>The TCP port number to accept on (defaults to <code>6001</code>).
+ * <dt><code>backlog</code>
+ * <dd>The maximum length of the acceptor's queue (defaults to
+ * <code>50</code>).
+ * <dt><code>tcpnodelay</code>
+ * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's
+ * algorithm on the resulting connection.
+ * </dl>
+ *
+ * @param connectionDescription the description of the connection.
+ * @return an <code>XConnection</code> to the client.
+ *
+ * @see com.sun.star.connection.XConnection
+ * @see com.sun.star.connection.XConnector
+ */
+ public XConnection accept(String connectionDescription) throws
+ AlreadyAcceptingException, ConnectionSetupException,
+ com.sun.star.lang.IllegalArgumentException
+ {
+ ServerSocket serv;
+ synchronized (this) {
+ if (server == null) {
+ ConnectionDescriptor desc
+ = new ConnectionDescriptor(connectionDescription);
+ String host = desc.getHost();
+ if (host.equals("0")) {
+ host = null;
+ }
+ if (DEBUG) {
+ System.err.println("##### " + getClass().getName()
+ + ".accept: creating ServerSocket "
+ + desc.getPort() + ", "
+ + desc.getBacklog() + ", " + host);
+ }
+ try {
+ server = new ServerSocket(desc.getPort(), desc.getBacklog(),
+ host == null ? null
+ : InetAddress.getByName(host));
+ } catch (IOException e) {
+ throw new ConnectionSetupException(e);
+ }
+ acceptingDescription = connectionDescription;
+ tcpNoDelay = desc.getTcpNoDelay();
+ } else if (!connectionDescription.equals(acceptingDescription)) {
+ throw new AlreadyAcceptingException(acceptingDescription
+ + " vs. "
+ + connectionDescription);
+ }
+ serv = server;
+ }
+ Socket socket = null;
+ try {
+ socket = serv.accept();
+ if (DEBUG) {
+ System.err.println("##### " + getClass().getName()
+ + ".accept: accepted " + socket);
+ }
+ // we enable tcpNoDelay for loopback connections because
+ // it can make a significant speed difference on linux boxes.
+ if (tcpNoDelay != null) {
+ socket.setTcpNoDelay(tcpNoDelay.booleanValue());
+ }
+ else {
+ InetSocketAddress address = (InetSocketAddress)socket.getRemoteSocketAddress();
+ if (address != null && address.getAddress().isLoopbackAddress()) {
+ socket.setTcpNoDelay(true);
+ }
+ }
+ return new SocketConnection(acceptingDescription, socket);
+ }
+ catch(IOException e) {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch(IOException ioException) {
+ }
+ }
+ throw new ConnectionSetupException(e);
+ }
+ }
+
+ /**
+ *
+ * @see com.sun.star.connection.XAcceptor#stopAccepting
+ */
+ public void stopAccepting() {
+ ServerSocket serv;
+ synchronized (this) {
+ serv = server;
+ }
+ try {
+ serv.close();
+ }
+ catch (IOException e) {
+ throw new com.sun.star.uno.RuntimeException(e);
+ }
+ }
+
+ private static final boolean DEBUG = false;
+
+ private ServerSocket server = null;
+ private String acceptingDescription;
+ private Boolean tcpNoDelay;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/connections/socket/socketConnector.java b/ridljar/com/sun/star/lib/connections/socket/socketConnector.java
new file mode 100644
index 000000000..c9a15d1f5
--- /dev/null
+++ b/ridljar/com/sun/star/lib/connections/socket/socketConnector.java
@@ -0,0 +1,174 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.connections.socket;
+
+import com.sun.star.comp.loader.FactoryHelper;
+import com.sun.star.connection.ConnectionSetupException;
+import com.sun.star.connection.NoConnectException;
+import com.sun.star.connection.XConnection;
+import com.sun.star.connection.XConnector;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+import com.sun.star.registry.XRegistryKey;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ * A component that implements the <code>XConnector</code> interface.
+ *
+ * <p>The <code>socketConnector</code> is a specialized component that uses TCP
+ * sockets for communication. The <code>socketConnector</code> is generally
+ * used by the <code>com.sun.star.connection.Connector</code> service.</p>
+ *
+ * @see com.sun.star.connection.XAcceptor
+ * @see com.sun.star.connection.XConnection
+ * @see com.sun.star.connection.XConnector
+ * @see com.sun.star.comp.loader.JavaLoader
+ *
+ * @since UDK 1.0
+ */
+public final class socketConnector implements XConnector {
+ /**
+ * The name of the service.
+ *
+ * <p>The <code>JavaLoader</code> accesses this through reflection.</p>
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static final String __serviceName
+ = "com.sun.star.connection.socketConnector";
+
+ /**
+ * Returns a factory for creating the service.
+ *
+ * <p>This method is called by the <code>JavaLoader</code>.</p>
+ *
+ * @param implName the name of the implementation for which a service is
+ * requested.
+ * @param multiFactory the service manager to be used (if needed).
+ * @param regKey the registry key.
+ * @return an <code>XSingleServiceFactory</code> for creating the component.
+ *
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleServiceFactory __getServiceFactory(
+ String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey)
+ {
+ return implName.equals(socketConnector.class.getName())
+ ? FactoryHelper.getServiceFactory(socketConnector.class,
+ __serviceName, multiFactory,
+ regKey)
+ : null;
+ }
+
+ /**
+ * Connects via the described socket to a waiting server.
+ *
+ * <p>The connection description has the following format:
+ * <code><var>type</var></code><!--
+ * -->*(<code><var>key</var>=<var>value</var></code>),
+ * where <code><var>type</var></code> should be <code>socket</code>
+ * (ignoring case). Supported keys (ignoring case) currently are</p>
+ * <dl>
+ * <dt><code>host</code>
+ * <dd>The name or address of the server. Must be present.
+ * <dt><code>port</code>
+ * <dd>The TCP port number of the server (defaults to <code>6001</code>).
+ * <dt><code>tcpnodelay</code>
+ * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's
+ * algorithm on the resulting connection.
+ * </dl>
+ *
+ * @param connectionDescription the description of the connection.
+ * @return an <code>XConnection</code> to the server.
+ *
+ * @see com.sun.star.connection.XAcceptor
+ * @see com.sun.star.connection.XConnection
+ */
+ public synchronized XConnection connect(String connectionDescription)
+ throws NoConnectException, ConnectionSetupException
+ {
+ if (connected)
+ throw new ConnectionSetupException("already connected");
+
+ ConnectionDescriptor desc;
+ try {
+ desc = new ConnectionDescriptor(connectionDescription);
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new ConnectionSetupException(e);
+ }
+
+ if (desc.getHost() == null)
+ throw new ConnectionSetupException("host parameter missing");
+ // Try all (IPv4 and IPv6) addresses, in case this client is on a
+ // dual-stack host and the server process is an IPv4-only process, also
+ // on a dual-stack host (see Stevens, Fenner, Rudoff: "Unix Network
+ // Programming, Volume 1: The Sockets Networking API, 3rd Edition",
+ // p. 359):
+ InetAddress[] adr;
+ try {
+ adr = InetAddress.getAllByName(desc.getHost());
+ } catch (UnknownHostException e) {
+ throw new ConnectionSetupException(e);
+ }
+ Socket socket = null;
+ boolean isLoopbackAddress = false;
+ for (int i = 0; i < adr.length; ++i) {
+ try {
+ isLoopbackAddress = adr[i].isLoopbackAddress();
+ socket = new Socket(adr[i], desc.getPort());
+ break;
+ } catch (IOException e) {
+ if (i == adr.length - 1)
+ throw new NoConnectException(e);
+ }
+ }
+
+ if (socket == null)
+ throw new ConnectionSetupException("no socket");
+
+ XConnection con;
+ try {
+ // we enable tcpNoDelay for loopback connections because
+ // it can make a significant speed difference on linux boxes.
+ if (desc.getTcpNoDelay() != null)
+ socket.setTcpNoDelay(desc.getTcpNoDelay().booleanValue());
+ else if (isLoopbackAddress)
+ socket.setTcpNoDelay(true);
+
+ con = new SocketConnection(connectionDescription, socket);
+ } catch (IOException e) {
+ try {
+ socket.close();
+ } catch(IOException ioException) {
+ }
+ throw new NoConnectException(e);
+ }
+ connected = true;
+ return con;
+ }
+
+ private boolean connected = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/Proxy.java b/ridljar/com/sun/star/lib/uno/Proxy.java
new file mode 100644
index 000000000..7d3612758
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/Proxy.java
@@ -0,0 +1,34 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno;
+
+/**
+ * Marker interface implemented by proxies for UNO objects.
+ *
+ * <p>Currently, this interface is used internally by
+ * <code>com.sun.star.lib.uno.environments.java.java_environment</code> to
+ * distinguish between proxies and local objects. Any proxy object that shall
+ * be registered at the <code>java_environment</code> must implement this marker
+ * interface.</p>
+ */
+public interface Proxy {
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java
new file mode 100644
index 000000000..2d3e9a848
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.adapter;
+
+import com.sun.star.io.XInputStream;
+import com.sun.star.io.XSeekable;
+import com.sun.star.lib.uno.helper.ComponentBase;
+
+public final class ByteArrayToXInputStreamAdapter
+ extends ComponentBase
+ implements XInputStream, XSeekable
+{
+
+ byte[] m_bytes;
+ int m_length;
+ int m_pos;
+
+ boolean m_open;
+
+ /** Creates a new instance of ByteArrayXInputStram */
+ public ByteArrayToXInputStreamAdapter(byte[] bytes) {
+ init(bytes);
+ }
+
+ public void init(byte[] bytes) {
+ m_bytes = bytes;
+ m_length = bytes.length;
+ m_pos = 0;
+ m_open = true;
+ }
+
+ private void _check() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException {
+ if (m_bytes == null) {
+ throw new com.sun.star.io.NotConnectedException("no bytes");
+ }
+ if(!m_open) {
+ throw new com.sun.star.io.IOException("input closed");
+ }
+ }
+
+ public int available() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException {
+ _check();
+ long a = m_length - m_pos;
+ if (a != (int)a)
+ throw new com.sun.star.io.IOException("integer overflow");
+ else {
+ return (int)a;
+ }
+ }
+
+ public void closeInput() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException {
+ _check();
+ m_open = false;
+ }
+
+ public int readBytes(byte[][] values, int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException {
+ _check();
+ try {
+ int remain = (m_length - m_pos);
+ if (param > remain) param = remain;
+ /* ARGH!!! */
+ if (values[0] == null){
+ values[0] = new byte[param];
+ }
+ System.arraycopy(m_bytes, m_pos, values[0], 0, param);
+ m_pos += param;
+ return param;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ throw new com.sun.star.io.BufferSizeExceededException(ex, "buffer overflow");
+ } catch (Exception ex) {
+ throw new com.sun.star.io.IOException(ex, "error accessing buffer");
+ }
+ }
+
+ public int readSomeBytes(byte[][] values, int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException {
+ return readBytes(values, param);
+ }
+
+ public void skipBytes(int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException {
+ _check();
+ if (param > (m_length - m_pos))
+ throw new com.sun.star.io.BufferSizeExceededException("buffer overflow");
+ m_pos += param;
+ }
+
+ public long getLength() throws com.sun.star.io.IOException {
+ if (m_bytes != null) return m_length;
+ else throw new com.sun.star.io.IOException("no bytes");
+ }
+
+ public long getPosition() throws com.sun.star.io.IOException {
+ if (m_bytes != null) return m_pos;
+ else throw new com.sun.star.io.IOException("no bytes");
+ }
+
+ public void seek(long param) throws com.sun.star.lang.IllegalArgumentException, com.sun.star.io.IOException {
+ if (m_bytes != null){
+ if (param < 0 || param > m_length) throw new com.sun.star.lang.IllegalArgumentException("invalid seek position");
+ else m_pos = (int)param;
+ }else throw new com.sun.star.io.IOException("no bytes");
+ }
+}
diff --git a/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java
new file mode 100644
index 000000000..3d60e5a11
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.adapter;
+
+import java.io.IOException;
+
+import com.sun.star.io.XInputStream;
+
+import java.io.InputStream;
+
+/** The <code>InputStreamToInputXStreamAdapter</code> wraps the
+ Java <code>InputStream</code> object into a
+ UNO <code>XInputStream</code> object.
+ This allows users to access an <code>InputStream</code>
+ as if it were an <code>XInputStream</code>.
+ */
+public final class InputStreamToXInputStreamAdapter implements XInputStream {
+
+ /**
+ * Internal store to the InputStream
+ */
+ private final InputStream iIn;
+
+ /**
+ * Constructor.
+ *
+ * @param in The <code>XInputStream</code> to be
+ * accessed as an <code>InputStream</code>.
+ */
+ public InputStreamToXInputStreamAdapter (InputStream in)
+ {
+ iIn = in;
+ }
+
+ public int available() throws
+ com.sun.star.io.IOException
+ {
+
+ int bytesAvail;
+
+ try {
+ bytesAvail = iIn.available();
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+
+ return bytesAvail;
+ }
+
+ public void closeInput() throws
+ com.sun.star.io.IOException
+ {
+ try {
+ iIn.close();
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+ }
+
+ public int readBytes(byte[][] b, int len) throws
+ com.sun.star.io.IOException
+ {
+ try {
+ long bytesRead;
+ if (len >iIn.available()) {
+ bytesRead = iIn.read(b[0], 0, iIn.available());
+ }
+ else{
+ bytesRead = iIn.read(b[0], 0, len);
+ }
+ // Casting bytesRead to an int is okay, since the user can
+ // only pass in an integer length to read, so the bytesRead
+ // must <= len.
+
+ if (bytesRead <= 0) {
+ return 0;
+ }
+ return ((int)bytesRead);
+
+
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException("reader error", e);
+ }
+ }
+
+ public int readSomeBytes(byte[][] b, int len) throws
+ com.sun.star.io.IOException
+ {
+ try {
+ long bytesRead;
+ if (len >iIn.available()) {
+ bytesRead = iIn.read(b[0], 0, iIn.available());
+ }
+ else{
+ bytesRead = iIn.read(b[0], 0, len);
+ }
+ // Casting bytesRead to an int is okay, since the user can
+ // only pass in an integer length to read, so the bytesRead
+ // must <= len.
+
+ if (bytesRead <= 0) {
+ return 0;
+ }
+ return ((int)bytesRead);
+
+
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException("reader error", e);
+ }
+ }
+
+ public void skipBytes(int n) throws
+ com.sun.star.io.IOException
+ {
+ try {
+ iIn.available();
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+
+ do {
+ try {
+ n -= iIn.skip(n);
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+ } while (n > 0);
+ }
+}
+
diff --git a/ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java
new file mode 100644
index 000000000..2842e3df0
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.adapter;
+
+import java.io.IOException;
+
+import com.sun.star.io.XOutputStream;
+
+import java.io.OutputStream;
+
+/** The <code>OutputStreamToXOutputStreamAdapter</code> wraps
+ a UNO <code>XOutputStream</code> into a Java <code>OutputStream</code>
+ object in a Java. This allows users to access an <code>OutputStream</code>
+ as if it were an <code>XOutputStream</code>.
+ */
+public final class OutputStreamToXOutputStreamAdapter implements XOutputStream {
+
+ /**
+ * Internal handle to the OutputStream
+ */
+ OutputStream iOut;
+
+ /**
+ * Constructor.
+ *
+ * @param out The <code>XOutputStream</code> to be
+ * accessed as an <code>OutputStream</code>.
+ */
+ public OutputStreamToXOutputStreamAdapter(OutputStream out) {
+ iOut = out;
+ }
+
+ public void closeOutput() throws
+ com.sun.star.io.IOException
+ {
+ try {
+ iOut.close();
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+ }
+
+ public void flush() throws
+ com.sun.star.io.IOException
+ {
+ try {
+ iOut.flush();
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+ }
+
+ public void writeBytes(byte[] b) throws
+ com.sun.star.io.IOException
+ {
+
+ try {
+ iOut.write(b);
+ } catch (IOException e) {
+ throw new com.sun.star.io.IOException(e);
+ }
+ }
+
+}
diff --git a/ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java
new file mode 100644
index 000000000..25f1798fb
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.adapter;
+
+import java.io.IOException;
+
+import com.sun.star.io.XInputStream;
+
+import java.io.InputStream;
+
+/**
+ * The <code>XInputStreamToInputStreamAdapter</code> wraps
+ * the UNO <code>XInputStream</code> object in a Java
+ * <code>InputStream</code>. This allows users to access
+ * an <code>XInputStream</code> as if it were an
+ * <code>InputStream</code>.
+ */
+public final class XInputStreamToInputStreamAdapter extends InputStream {
+
+ /**
+ * Internal handle to the XInputStream
+ */
+ private final XInputStream xin;
+
+ /**
+ * Constructor.
+ *
+ * @param in The <code>XInputStream</code> to be
+ * accessed as an <code>InputStream</code>.
+ */
+ public XInputStreamToInputStreamAdapter (XInputStream in) {
+ xin = in;
+ }
+
+ @Override
+ public int available() throws IOException {
+
+ int bytesAvail;
+
+ try {
+ bytesAvail = xin.available();
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+
+ return bytesAvail;
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ xin.closeInput();
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public int read () throws IOException {
+ byte [][] tmp = new byte [1][1];
+ try {
+ long bytesRead = xin.readBytes(tmp, 1);
+
+ if (bytesRead <= 0) {
+ return (-1);
+ } else {
+ int tmpInt = tmp[0][0];
+ if (tmpInt< 0 ){
+ tmpInt = 256 +tmpInt;
+ }
+ return tmpInt;
+ }
+
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public int read (byte[] b) throws IOException {
+
+ byte [][] tmp = new byte [1][b.length];
+ int bytesRead;
+
+ try {
+ bytesRead = xin.readBytes(tmp, b.length);
+ if (bytesRead <= 0) {
+ return -1;
+ } else if (bytesRead < b.length) {
+ System.arraycopy(tmp[0], 0, b, 0, bytesRead);
+ } else {
+ System.arraycopy(tmp[0], 0, b, 0, b.length);
+ }
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+
+ return bytesRead;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ byte [][] tmp = new byte [1][b.length];
+ try {
+ long bytesRead;
+ int av = xin.available();
+ if ( av != 0 && len > av) {
+ bytesRead = xin.readBytes(tmp, av);
+ }
+ else{
+ bytesRead = xin.readBytes(tmp,len);
+ }
+ // Casting bytesRead to an int is okay, since the user can
+ // only pass in an integer length to read, so the bytesRead
+ // must <= len.
+
+ if (bytesRead <= 0) {
+ return -1;
+ } else if (bytesRead < len) {
+ System.arraycopy(tmp[0], 0, b, off, (int)bytesRead);
+ } else {
+ System.arraycopy(tmp[0], 0, b, off, len);
+ }
+
+ return ((int)bytesRead);
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+
+ int avail;
+ long tmpLongVal = n;
+ int tmpIntVal;
+
+ try {
+ avail = xin.available();
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+
+ do {
+ if (tmpLongVal >= Integer.MAX_VALUE) {
+ tmpIntVal = Integer.MAX_VALUE;
+ } else {
+ // Casting is safe here.
+ tmpIntVal = (int)tmpLongVal;
+ }
+ tmpLongVal -= tmpIntVal;
+
+ try {
+ xin.skipBytes(tmpIntVal);
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ } while (tmpLongVal > 0);
+
+ if ( avail != 0 && avail < n) {
+ return avail;
+ } else {
+ return n;
+ }
+ }
+
+ /**
+ * Tests if this input stream supports the mark and reset methods.
+ * The markSupported method of
+ * <code>XInputStreamToInputStreamAdapter</code> returns false.
+ *
+ * @return false
+ */
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ // Not supported.
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ // Not supported.
+ }
+}
+
diff --git a/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java
new file mode 100644
index 000000000..48871277a
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.adapter;
+
+import com.sun.star.io.*;
+import com.sun.star.lib.uno.helper.ComponentBase;
+
+public final class XOutputStreamToByteArrayAdapter
+ extends ComponentBase
+ implements XOutputStream
+{
+ private static final int initialSize = 100240; // 10 kb
+ private int size = 0;
+ private int position = 0;
+ private boolean externalBuffer = false;
+ private byte[] buffer;
+
+ /** Creates a new instance of ByteArrayXOutputStream */
+ public XOutputStreamToByteArrayAdapter() {
+ this(null);
+ }
+
+ public XOutputStreamToByteArrayAdapter(byte[] aBuffer) {
+ if (aBuffer != null) {
+ externalBuffer = true;
+ buffer = aBuffer;
+ size = buffer.length;
+ } else {
+ size = initialSize;
+ buffer = new byte[size];
+ }
+ }
+
+ public byte[] getBuffer() {
+ return buffer;
+ }
+
+ public void closeOutput()
+ throws com.sun.star.io.NotConnectedException,
+ com.sun.star.io.BufferSizeExceededException,
+ com.sun.star.io.IOException
+ {
+ // trim buffer
+ if ( buffer.length > position && !externalBuffer )
+ {
+ byte[] newBuffer = new byte[position];
+ System.arraycopy(buffer, 0, newBuffer, 0, position);
+ buffer = newBuffer;
+ }
+ }
+
+ public void flush()
+ throws com.sun.star.io.NotConnectedException,
+ com.sun.star.io.BufferSizeExceededException,
+ com.sun.star.io.IOException
+ {
+ }
+
+ public void writeBytes(byte[] values)
+ throws com.sun.star.io.NotConnectedException,
+ com.sun.star.io.BufferSizeExceededException,
+ com.sun.star.io.IOException
+ {
+ if ( values.length > size-position )
+ {
+ if ( externalBuffer )
+ throw new BufferSizeExceededException("out of buffer space, cannot grow external buffer");
+ while ( values.length > size-position ) {
+ size *= 2;
+ }
+ byte[] newBuffer = new byte[size];
+ System.arraycopy(buffer, 0, newBuffer, 0, position);
+ buffer = newBuffer;
+ }
+ System.arraycopy(values, 0, buffer, position, values.length);
+ position += values.length;
+ }
+
+}
diff --git a/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java
new file mode 100644
index 000000000..8104cf42f
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.adapter;
+
+import java.io.IOException;
+
+import com.sun.star.io.XOutputStream;
+
+import java.io.OutputStream;
+
+/**
+ * The <code>XOutputStreamToOutputStreamAdapter</code> wraps
+ * the UNO <code>XOutputStream</code> object in a Java
+ * <code>OutputStream</code>. This allows users to access
+ * an <code>XOutputStream</code> as if it were an
+ * <code>OutputStream</code>.
+ */
+public final class XOutputStreamToOutputStreamAdapter extends OutputStream {
+
+ /**
+ * Internal handle to the XInputStream
+ */
+ XOutputStream xout;
+
+ /**
+ * Constructor.
+ *
+ * @param out The <code>XOutputStream</code> to be
+ * accessed as an <code>OutputStream</code>.
+ */
+ public XOutputStreamToOutputStreamAdapter(XOutputStream out) {
+ xout = out;
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ xout.closeOutput();
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ try {
+ xout.flush();
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+
+ try {
+ xout.writeBytes(b);
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+
+ byte[] tmp = new byte[len];
+
+ // Copy the input array into a temp array, and write it out.
+
+ System.arraycopy(b, off, tmp, 0, len);
+
+ try {
+ xout.writeBytes(tmp);
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+
+ byte [] oneByte = new byte [1];
+ oneByte[0] = (byte) b;
+
+ try {
+ xout.writeBytes(oneByte);
+ } catch (Exception e) {
+ IOException newEx = new IOException(e.getMessage());
+ newEx.initCause(e);
+ throw newEx;
+ }
+ }
+}
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java
new file mode 100644
index 000000000..0a724f059
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java
@@ -0,0 +1,43 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.bridges.java_remote;
+
+import com.sun.star.bridge.XBridge;
+
+/**
+ * A back door to access the bridge associated with a bridged object.
+ */
+public final class BridgedObject {
+ /**
+ * Obtains the bridge associated with a bridged object.
+ *
+ * @param obj a reference to a (Java representation of a) UNO object;
+ * must not be null.
+ * @return the bridge associated with the given object, if it is indeed
+ * bridged; otherwise, null is returned.
+ */
+ public static XBridge getBridge(Object obj) {
+ return ProxyFactory.getBridge(obj);
+ }
+
+ private BridgedObject() {} // do not instantiate
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java
new file mode 100644
index 000000000..1b3848983
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java
@@ -0,0 +1,198 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.bridges.java_remote;
+
+import com.sun.star.bridge.XBridge;
+import com.sun.star.lib.util.AsynchronousFinalizer;
+import com.sun.star.uno.IQueryInterface;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.UnoRuntime;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * A factory for proxies specific to the <code>java_remote_bridge</code>.
+ *
+ * <p>Eventually, this class should be united with all other proxy classes
+ * specific to certain bridges (for example, the JNI bridge), resulting in a
+ * generic proxy class.</p>
+ */
+final class ProxyFactory {
+ public ProxyFactory(RequestHandler requestHandler, XBridge bridge) {
+ this.requestHandler = requestHandler;
+ this.bridge = bridge;
+ }
+
+ public Object create(String oid, Type type) {
+ return Proxy.newProxyInstance(
+ getClass().getClassLoader(),
+ new Class[] { com.sun.star.lib.uno.Proxy.class,
+ IQueryInterface.class, type.getZClass() },
+ new Handler(oid, type));
+ }
+
+ public boolean isProxy(Object obj) {
+ if (Proxy.isProxyClass(obj.getClass())) {
+ InvocationHandler h = Proxy.getInvocationHandler(obj);
+ return h instanceof Handler && ((Handler) h).matches(this);
+ } else {
+ return false;
+ }
+ }
+
+ public void dispose() throws InterruptedException {
+ asynchronousFinalizer.drain();
+ }
+
+ public static XBridge getBridge(Object obj) {
+ if (Proxy.isProxyClass(obj.getClass())) {
+ InvocationHandler h = Proxy.getInvocationHandler(obj);
+ if (h instanceof Handler) {
+ return ((Handler) h).getBridge();
+ }
+ }
+ return null;
+ }
+
+ static int getDebugCount() {
+ synchronized (debugCountLock) {
+ return debugCount;
+ }
+ }
+
+ private static void incrementDebugCount() {
+ synchronized (debugCountLock) {
+ ++debugCount;
+ }
+ }
+
+ private static void decrementDebugCount() {
+ synchronized (debugCountLock) {
+ --debugCount;
+ }
+ }
+
+ private final class Handler implements InvocationHandler {
+ public Handler(String oid, Type type) {
+ this.oid = oid;
+ this.type = type;
+ incrementDebugCount();
+ }
+
+ public boolean matches(ProxyFactory factory) {
+ return ProxyFactory.this == factory;
+ }
+
+ public XBridge getBridge() {
+ return bridge;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ if (method.equals(METHOD_EQUALS) || method.equals(METHOD_IS_SAME)) {
+ return Boolean.valueOf(args[0] != null
+ && oid.equals(UnoRuntime.generateOid(args[0])));
+ } else if (method.equals(METHOD_HASH_CODE)) {
+ return Integer.valueOf(oid.hashCode());
+ } else if (method.equals(METHOD_TO_STRING)) {
+ return "[Proxy:" + System.identityHashCode(proxy) + "," + oid
+ + "," + type + "]";
+ } else if (method.equals(METHOD_QUERY_INTERFACE)) {
+ // See the comment in java_remote_bridge.mapInterfaceTo for one
+ // reason why this implementation must not satisfy a request for
+ // a super-interface with a proxy itself:
+ return args[0].equals(type) ? proxy
+ : request("queryInterface", args);
+ } else if (method.equals(METHOD_GET_OID)) {
+ return oid;
+ } else {
+ return request(method.getName(), args);
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ decrementDebugCount();
+ asynchronousFinalizer.add(new AsynchronousFinalizer.Job() {
+ public void run() throws Throwable {
+ request("release", null);
+ }
+ });
+ }
+
+ private Object request(String operation, Object[] args) throws Throwable
+ {
+ Object res = requestHandler.sendRequest(oid, type, operation, args);
+ // Avoid early finalization of this object, while an invoke ->
+ // request call is still ongoing; as finalize also calls request,
+ // this should fulfil the condition from The Java Language
+ // Specification, 3rd ed., that "if an object's finalizer can result
+ // in synchronization on that object, then that object must be alive
+ // and considered reachable whenever a lock is held on it:"
+ synchronized (this) {
+ ++dummy;
+ }
+ return res;
+ }
+
+ private final String oid;
+ private final Type type;
+ @SuppressWarnings("unused")
+ private int dummy = 0;
+ }
+
+ private static final Method METHOD_EQUALS;
+ private static final Method METHOD_HASH_CODE;
+ private static final Method METHOD_TO_STRING;
+ private static final Method METHOD_QUERY_INTERFACE;
+ private static final Method METHOD_IS_SAME;
+ private static final Method METHOD_GET_OID;
+ static {
+ try {
+ METHOD_EQUALS = Object.class.getMethod(
+ "equals", new Class[] { Object.class });
+ METHOD_HASH_CODE = Object.class.getMethod(
+ "hashCode", (Class[]) null);
+ METHOD_TO_STRING = Object.class.getMethod(
+ "toString", (Class[]) null);
+ METHOD_QUERY_INTERFACE = IQueryInterface.class.getMethod(
+ "queryInterface", new Class[] { Type.class });
+ METHOD_IS_SAME = IQueryInterface.class.getMethod(
+ "isSame", new Class[] { Object.class });
+ METHOD_GET_OID = IQueryInterface.class.getMethod(
+ "getOid", (Class[]) null);
+ } catch (NoSuchMethodException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private static final Object debugCountLock = new Object();
+ private static int debugCount = 0;
+
+ private final RequestHandler requestHandler;
+ private final XBridge bridge;
+ private final AsynchronousFinalizer asynchronousFinalizer =
+ new AsynchronousFinalizer();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java
new file mode 100644
index 000000000..d5246bf26
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java
@@ -0,0 +1,35 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.bridges.java_remote;
+
+import com.sun.star.uno.Type;
+
+/**
+ * The link between the proxies generated by <code>ProxyFactory</code> (which
+ * receive requests in the form of method calls) and
+ * <code>java_remote_bridge</code> (which passes those requests on to the remote
+ * side).
+ */
+interface RequestHandler {
+ Object sendRequest(String oid, Type type, String operation, Object[] args)
+ throws Throwable;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java
new file mode 100644
index 000000000..8d660e88c
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java
@@ -0,0 +1,76 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.bridges.java_remote;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.sun.star.connection.XConnection;
+
+
+class XConnectionInputStream_Adapter extends InputStream {
+ private static final boolean DEBUG = false;
+
+ protected XConnection _xConnection;
+ protected byte _bytes[][] = new byte[1][];
+
+ XConnectionInputStream_Adapter(XConnection xConnection) {
+ if(xConnection == null) throw new NullPointerException("the XConnection must not be null");
+
+ if(DEBUG) System.err.println("#### " + getClass().getName() + " - instantiated ");
+
+ _xConnection = xConnection;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int len;
+
+ try {
+ len = _xConnection.read(_bytes, 1);
+ } catch(com.sun.star.io.IOException ioException) {
+ IOException ex = new IOException(ioException.getMessage());
+ ex.initCause(ioException);
+ throw ex;
+ }
+
+ if(DEBUG) System.err.println("#### " + getClass().getName() + " - one byte read:" + _bytes[0][0]);
+
+ return len == 0 ? -1 : _bytes[0][0] & 0xff;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ try {
+ len = _xConnection.read(_bytes, len - off);
+ } catch(com.sun.star.io.IOException ioException) {
+ IOException ex = new IOException(ioException.getMessage());
+ ex.initCause(ioException);
+ throw ex;
+ }
+
+ System.arraycopy(_bytes[0], 0, b, off, len);
+
+ return len == 0 ? -1 : len;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java
new file mode 100644
index 000000000..ac198f8fd
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java
@@ -0,0 +1,89 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.bridges.java_remote;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.sun.star.connection.XConnection;
+
+
+class XConnectionOutputStream_Adapter extends OutputStream {
+ private static final boolean DEBUG = false;
+
+ protected XConnection _xConnection;
+ protected byte _bytes[] = new byte[1];
+
+ XConnectionOutputStream_Adapter(XConnection xConnection) {
+ if(DEBUG) System.err.println("#### " + this.getClass() + " - instantiated ");
+
+ _xConnection = xConnection;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ _bytes[0] = (byte)b;
+
+ try {
+ _xConnection.write(_bytes);
+ } catch(com.sun.star.io.IOException ioException) {
+ IOException ex = new IOException(ioException.getMessage());
+ ex.initCause(ioException);
+ throw ex;
+ }
+
+ if(DEBUG) System.err.println("#### " + this.getClass() + " - one byte written:" + _bytes[0]);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ byte bytes[] ;
+
+ if(off == 0 && len == b.length) {
+ bytes = b;
+ } else {
+ bytes = new byte[len];
+
+ System.arraycopy(b, off, bytes, 0, len);
+ }
+
+ try {
+ _xConnection.write(bytes);
+ } catch(com.sun.star.io.IOException ioException) {
+ IOException ex = new IOException(ioException.getMessage());
+ ex.initCause(ioException);
+ throw ex;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ try {
+ _xConnection.flush();
+ } catch(com.sun.star.io.IOException ioException) {
+ IOException ex = new IOException(ioException.getMessage());
+ ex.initCause(ioException);
+ throw ex;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
new file mode 100644
index 000000000..392e031e3
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
@@ -0,0 +1,700 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.bridges.java_remote;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.sun.star.bridge.XBridge;
+import com.sun.star.bridge.XInstanceProvider;
+import com.sun.star.connection.XConnection;
+import com.sun.star.lang.DisposedException;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XEventListener;
+import com.sun.star.lib.uno.environments.java.java_environment;
+import com.sun.star.lib.uno.environments.remote.IProtocol;
+import com.sun.star.lib.uno.environments.remote.IReceiver;
+import com.sun.star.lib.uno.environments.remote.IThreadPool;
+import com.sun.star.lib.uno.environments.remote.Job;
+import com.sun.star.lib.uno.environments.remote.Message;
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.environments.remote.ThreadPoolManager;
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.lib.util.DisposeListener;
+import com.sun.star.lib.util.DisposeNotifier;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.IBridge;
+import com.sun.star.uno.IEnvironment;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+/**
+ * This class implements a remote bridge.
+ *
+ * <p>Therefore various interfaces are implemented.</p>
+ *
+ * <p>The protocol to used is passed by name, the bridge
+ * then looks for it under <code>com.sun.star.lib.uno.protocols</code>.</p>
+ *
+ * @since UDK1.0
+ */
+public class java_remote_bridge
+ implements IBridge, IReceiver, RequestHandler, XBridge, XComponent,
+ DisposeNotifier
+{
+ /**
+ * When set to true, enables various debugging output.
+ */
+ private static final boolean DEBUG = false;
+
+ private final class MessageDispatcher extends Thread {
+ public MessageDispatcher() {
+ super("MessageDispatcher");
+ }
+
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ synchronized (this) {
+ if (terminate) {
+ break;
+ }
+ }
+ Message msg = _iProtocol.readMessage();
+ Object obj = null;
+ if (msg.isRequest()) {
+ String oid = msg.getObjectId();
+ Type type = new Type(msg.getType());
+ int fid = msg.getMethod().getIndex();
+ if (fid == MethodDescription.ID_RELEASE) {
+ _java_environment.revokeInterface(oid, type);
+ remRefHolder(type, oid);
+ if (msg.isSynchronous()) {
+ sendReply(false, msg.getThreadId(), null);
+ }
+ continue;
+ }
+ obj = _java_environment.getRegisteredInterface(
+ oid, type);
+ if (obj == null
+ && fid == MethodDescription.ID_QUERY_INTERFACE)
+ {
+ if (_xInstanceProvider == null) {
+ sendReply(
+ true, msg.getThreadId(),
+ new com.sun.star.uno.RuntimeException(
+ "unknown OID " + oid));
+ continue;
+ } else {
+ UnoRuntime.setCurrentContext(
+ msg.getCurrentContext());
+ try {
+ obj = _xInstanceProvider.getInstance(oid);
+ } catch (com.sun.star.uno.RuntimeException e) {
+ sendReply(true, msg.getThreadId(), e);
+ continue;
+ } catch (Exception e) {
+ sendReply(
+ true, msg.getThreadId(),
+ new com.sun.star.uno.RuntimeException(
+ e.toString()));
+ continue;
+ } finally {
+ UnoRuntime.setCurrentContext(null);
+ }
+ }
+ }
+ }
+ _iThreadPool.putJob(
+ new Job(obj, java_remote_bridge.this, msg));
+ }
+ } catch (Throwable e) {
+ dispose(e);
+ }
+ }
+
+ public synchronized void terminate() {
+ terminate = true;
+ }
+
+ private boolean terminate = false;
+ }
+
+ protected XConnection _xConnection;
+
+ protected XInstanceProvider _xInstanceProvider;
+
+ protected String _name = "remote";
+ private final String protocol;
+ protected IProtocol _iProtocol;
+ protected IEnvironment _java_environment;
+ protected MessageDispatcher _messageDispatcher;
+ protected final AtomicInteger _life_count = new AtomicInteger(); // determines if this bridge is alive, which is controlled by acquire and release calls
+
+ private final ArrayList<XEventListener> _listeners = new ArrayList<XEventListener>();
+
+ protected IThreadPool _iThreadPool;
+
+ // Variable disposed must only be used while synchronized on this object:
+ private boolean disposed = false;
+
+ /**
+ * This method is for testing only.
+ */
+ int getLifeCount() {
+ return _life_count.get();
+ }
+
+ /**
+ * This method is for testing only.
+ */
+ IProtocol getProtocol() {
+ return _iProtocol;
+ }
+
+ /**
+ * The ref holder stuff strongly holds objects mapped out via this bridge
+ * (the java_environment only holds them weakly).
+ *
+ * <p>When this bridge is disposed, all remaining ref holder entries are
+ * released.</p>
+ */
+ private static final class RefHolder {
+ public RefHolder(Type type, Object object) {
+ this.type = type;
+ this.object = object;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public void acquire() {
+ ++count;
+ }
+
+ public boolean release() {
+ return --count == 0;
+ }
+
+ private final Type type;
+ @SuppressWarnings("unused")
+ private final Object object;
+ private int count = 1;
+ }
+
+ private final HashMap<String, LinkedList<RefHolder>> refHolders = new HashMap<String, LinkedList<RefHolder>>();
+ // from OID (String) to LinkedList of RefHolder
+
+ private boolean hasRefHolder(String oid, Type type) {
+ synchronized (refHolders) {
+ LinkedList<RefHolder> l = refHolders.get(oid);
+ if (l != null) {
+ for (RefHolder rh : l) {
+ if (type.isSupertypeOf(rh.getType())) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ final void addRefHolder(Object obj, Type type, String oid) {
+ synchronized (refHolders) {
+ LinkedList<RefHolder> l = refHolders.get(oid);
+ if (l == null) {
+ l = new LinkedList<RefHolder>();
+ refHolders.put(oid, l);
+ }
+ boolean found = false;
+ for (Iterator<RefHolder> i = l.iterator(); !found && i.hasNext();) {
+ RefHolder rh = i.next();
+ if (rh.getType().equals(type)) {
+ found = true;
+ rh.acquire();
+ }
+ }
+ if (!found) {
+ l.add(new RefHolder(type, obj));
+ }
+ }
+ acquire();
+ }
+
+ final void remRefHolder(Type type, String oid) {
+ synchronized (refHolders) {
+ LinkedList<RefHolder> l = refHolders.get(oid);
+ if (l == null) {
+ return;
+ }
+ for (RefHolder rh : l) {
+ if (rh.getType().equals(type)) {
+ try {
+ if (rh.release()) {
+ l.remove(rh);
+ if (l.isEmpty()) {
+ refHolders.remove(oid);
+ }
+ }
+ } finally {
+ release();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ final void freeHolders() {
+ synchronized (refHolders) {
+ for (Iterator<Map.Entry<String,LinkedList<RefHolder>>> i1 = refHolders.entrySet().iterator(); i1.hasNext();)
+ {
+ Map.Entry<String,LinkedList<RefHolder>> e = i1.next();
+ String oid = e.getKey();
+ LinkedList<RefHolder> l = e.getValue();
+ for (Iterator<RefHolder> i2 = l.iterator(); i2.hasNext();) {
+ RefHolder rh = i2.next();
+ for (boolean done = false; !done;) {
+ done = rh.release();
+ _java_environment.revokeInterface(oid, rh.getType());
+ release();
+ }
+ }
+ }
+ refHolders.clear();
+ }
+ }
+
+ public java_remote_bridge(
+ IEnvironment java_environment, IEnvironment remote_environment,
+ Object[] args)
+ throws Exception
+ {
+ _java_environment = java_environment;
+ String proto = (String) args[0];
+ _xConnection = (XConnection) args[1];
+ _xInstanceProvider = (XInstanceProvider) args[2];
+ if (args.length > 3) {
+ _name = (String) args[3];
+ }
+ String attr;
+ int i = proto.indexOf(',');
+ if (i >= 0) {
+ protocol = proto.substring(0, i);
+ attr = proto.substring(i + 1);
+ } else {
+ protocol = proto;
+ attr = null;
+ }
+ _iProtocol = (IProtocol) Class.forName(
+ "com.sun.star.lib.uno.protocols." + protocol + "." + protocol).
+ getConstructor(
+ new Class[] {
+ IBridge.class, String.class, InputStream.class,
+ OutputStream.class }).
+ newInstance(
+ new Object[] {
+ this, attr,
+ new XConnectionInputStream_Adapter(_xConnection),
+ new XConnectionOutputStream_Adapter(_xConnection) });
+ proxyFactory = new ProxyFactory(this, this);
+ _iThreadPool = ThreadPoolManager.create();
+ _messageDispatcher = new MessageDispatcher();
+ _messageDispatcher.start();
+ _iProtocol.init();
+ }
+
+ private void notifyListeners() {
+ EventObject eventObject = new EventObject(this);
+
+ Iterator<XEventListener> elements = _listeners.iterator();
+ while(elements.hasNext()) {
+ XEventListener xEventListener = elements.next();
+
+ try {
+ xEventListener.disposing(eventObject);
+ }
+ catch(com.sun.star.uno.RuntimeException runtimeException) {
+ // we are here not interested in any exceptions
+ }
+ }
+ }
+
+ /**
+ * Constructs a new bridge.
+ * <p> This method is not part of the provided <code>api</code>
+ * and should only be used by the UNO runtime.</p>
+ *
+ * @param args the custom parameters: arg[0] == protocol_name,
+ * arg[1] == xConnection, arg[2] == xInstanceProvider.
+ *
+ * @deprecated as of UDK 1.0
+ */
+ @Deprecated
+ public java_remote_bridge(Object args[]) throws Exception {
+ this(UnoRuntime.getEnvironment("java", null), UnoRuntime.getEnvironment("remote", null), args);
+ }
+
+ /**
+ *
+ * @see com.sun.star.uno.IBridge#mapInterfaceTo
+ */
+ public Object mapInterfaceTo(Object object, Type type) {
+ checkDisposed();
+ if (object == null) {
+ return null;
+ } else {
+ String[] oid = new String[1];
+ object = _java_environment.registerInterface(object, oid, type);
+ if (!proxyFactory.isProxy(object)) {
+ // This branch must be taken iff object either is no proxy at
+ // all or a proxy from some other bridge. There are objects
+ // that behave like objects for this bridge but that are not
+ // detected as such by proxyFactory.isProxy. The only known
+ // case of such objects is com.sun.star.comp.beans.Wrapper,
+ // which implements com.sun.star.lib.uno.Proxy and effectively
+ // is a second proxy around a proxy that can be from this
+ // bridge. For that case, there is no problem, however: Since
+ // the proxies generated by ProxyFactory send each
+ // queryInterface to the original object (i.e., they do not
+ // short-circuit requests for a super-interface to themselves),
+ // there will always be an appropriate ProxyFactory-proxy
+ // registered at the _java_environment, so that the object
+ // returned by _java_environment.registerInterface will never be
+ // a com.sun.star.comp.beans.Wrapper.
+ addRefHolder(object, type, oid[0]);
+ }
+ return oid[0];
+ }
+ }
+
+ /**
+ * Maps an object from destination environment to the source environment.
+ *
+ * @param oId the object to map.
+ * @param type the interface under which is to be mapped.
+ * @return the object in the source environment.
+ *
+ * @see com.sun.star.uno.IBridge#mapInterfaceFrom
+ */
+ public Object mapInterfaceFrom(Object oId, Type type) {
+ checkDisposed();
+ // TODO What happens if an exception is thrown after the call to
+ // acquire, but before it is guaranteed that a pairing release will be
+ // called eventually?
+ acquire();
+ String oid = (String) oId;
+ Object object = _java_environment.getRegisteredInterface(oid, type);
+ if (object == null) {
+ object = _java_environment.registerInterface(
+ proxyFactory.create(oid, type), new String[] { oid }, type);
+ // the proxy sends a release when finalized
+ } else if (!hasRefHolder(oid, type)) {
+ sendInternalRequest(oid, type, "release", null);
+ }
+ return object;
+ }
+
+ /**
+ * Gives the source environment.
+ *
+ * @return the source environment of this bridge.
+ * @see com.sun.star.uno.IBridge#getSourceEnvironment
+ */
+ public IEnvironment getSourceEnvironment() {
+ return _java_environment;
+ }
+
+ /**
+ * Gives the destination environment.
+ *
+ * @return the destination environment of this bridge.
+ * @see com.sun.star.uno.IBridge#getTargetEnvironment
+ */
+ public IEnvironment getTargetEnvironment() {
+ return null;
+ }
+
+ /**
+ * Increases the life count.
+ *
+ * @see com.sun.star.uno.IBridge#acquire
+ */
+ public void acquire() {
+ if(DEBUG) {
+ int x = _life_count.incrementAndGet();
+ System.err.println("##### " + getClass().getName() + ".acquire:" + x);
+ } else {
+ _life_count.incrementAndGet();
+ }
+ }
+
+ /**
+ * Decreases the life count.
+ *
+ * <p>If the life count drops to zero, the bridge disposes itself.</p>
+ *
+ * @see com.sun.star.uno.IBridge#release
+ */
+ public void release() {
+ int x = _life_count.decrementAndGet();
+ if (x <= 0) {
+ dispose(new Throwable("end of life"));
+ }
+ }
+
+ public void dispose() {
+ dispose(new Throwable("user dispose"));
+ }
+
+ private void dispose(Throwable throwable) {
+ synchronized (this) {
+ if (disposed) {
+ return;
+ }
+ disposed = true;
+ }
+
+ notifyListeners();
+ for (Iterator<DisposeListener> i = disposeListeners.iterator(); i.hasNext();) {
+ i.next().notifyDispose(this);
+ }
+
+ _iProtocol.terminate();
+
+ try {
+ _messageDispatcher.terminate();
+
+ try {
+ _xConnection.close();
+ } catch (com.sun.star.io.IOException e) {
+ System.err.println(
+ getClass().getName() + ".dispose - IOException:" + e);
+ }
+
+ if (Thread.currentThread() != _messageDispatcher
+ && _messageDispatcher.isAlive())
+ {
+ _messageDispatcher.join(1000);
+ if (_messageDispatcher.isAlive()) {
+ _messageDispatcher.interrupt();
+ _messageDispatcher.join();
+ }
+ }
+
+ // interrupt all jobs queued by this bridge
+ _iThreadPool.dispose(throwable);
+
+ // release all out-mapped objects and all in-mapped proxies:
+ freeHolders();
+ // assert _java_environment instanceof java_environment;
+ ((java_environment) _java_environment).revokeAllProxies();
+
+ proxyFactory.dispose();
+
+ if (DEBUG) {
+ if (_life_count.get() != 0) {
+ System.err.println(getClass().getName()
+ + ".dispose - life count (proxies left):"
+ + _life_count);
+ }
+ _java_environment.list();
+ }
+
+ // clear members
+ _xConnection = null;
+ _java_environment = null;
+ _messageDispatcher = null;
+ } catch (InterruptedException e) {
+ System.err.println(getClass().getName()
+ + ".dispose - InterruptedException:" + e);
+ }
+ }
+
+ /**
+ *
+ * @see com.sun.star.bridge.XBridge#getInstance
+ */
+ public Object getInstance(String instanceName) {
+ Type t = new Type(XInterface.class);
+ return sendInternalRequest(
+ instanceName, t, "queryInterface", new Object[] { t });
+ }
+
+ /**
+ * Gives the name of this bridge.
+ *
+ * @return the name of this bridge.
+ * @see com.sun.star.bridge.XBridge#getName
+ */
+ public String getName() {
+ return _name;
+ }
+
+ /**
+ * Gives a description of the connection type and protocol used.
+ *
+ * @return connection type and protocol.
+ * @see com.sun.star.bridge.XBridge#getDescription
+ */
+ public String getDescription() {
+ return protocol + "," + _xConnection.getDescription();
+ }
+
+ public void sendReply(boolean exception, ThreadId threadId, Object result) {
+ if (DEBUG) {
+ System.err.println("##### " + getClass().getName() + ".sendReply: "
+ + exception + " " + result);
+ }
+
+ checkDisposed();
+
+ try {
+ _iProtocol.writeReply(exception, threadId, result);
+ } catch (IOException e) {
+ dispose(e);
+ throw (DisposedException)
+ (new DisposedException("unexpected " + e).initCause(e));
+ } catch (RuntimeException e) {
+ dispose(e);
+ throw e;
+ } catch (Error e) {
+ dispose(e);
+ throw e;
+ }
+ }
+
+ public Object sendRequest(
+ String oid, Type type, String operation, Object[] params)
+ throws Throwable
+ {
+ Object result = null;
+
+ checkDisposed();
+
+ ThreadId threadId = _iThreadPool.getThreadId();
+ Object handle = _iThreadPool.attach(threadId);
+ try {
+ boolean sync;
+ try {
+ sync = _iProtocol.writeRequest(
+ oid, TypeDescription.getTypeDescription(type), operation,
+ threadId, params);
+ } catch (IOException e) {
+ dispose(e);
+ throw (DisposedException)
+ new DisposedException(e.toString()).initCause(e);
+ }
+ if (sync && Thread.currentThread() != _messageDispatcher) {
+ result = _iThreadPool.enter(handle, threadId);
+ }
+ } finally {
+ _iThreadPool.detach(handle, threadId);
+ if(operation.equals("release"))
+ release(); // kill this bridge, if this was the last proxy
+ }
+
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".sendRequest left:" + result);
+
+ // On the wire (at least in URP), the result of queryInterface is
+ // transported as an ANY, but in Java it shall be transported as a
+ // direct reference to the UNO object (represented as a Java Object),
+ // never boxed in a com.sun.star.uno.Any:
+ if (operation.equals("queryInterface") && result instanceof Any) {
+ Any a = (Any) result;
+ if (a.getType().getTypeClass() == TypeClass.INTERFACE) {
+ result = a.getObject();
+ } else {
+ result = null; // should never happen
+ }
+ }
+
+ return result;
+ }
+
+ private Object sendInternalRequest(
+ String oid, Type type, String operation, Object[] arguments)
+ {
+ try {
+ return sendRequest(oid, type, operation, arguments);
+ } catch (Error e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new RuntimeException("Unexpected " + e);
+ }
+ }
+
+ /**
+ * Methods XComponent.
+ */
+ public void addEventListener(XEventListener xEventListener) {
+ _listeners.add(xEventListener);
+ }
+
+ public void removeEventListener(XEventListener xEventListener) {
+ _listeners.remove(xEventListener);
+ }
+
+ /**
+ *
+ * @see DisposeNotifier#addDisposeListener
+ */
+ public void addDisposeListener(DisposeListener listener) {
+ synchronized (this) {
+ if (!disposed) {
+ disposeListeners.add(listener);
+ return;
+ }
+ }
+ listener.notifyDispose(this);
+ }
+
+ /**
+ * This function must only be called while synchronized on this object.
+ */
+ private synchronized void checkDisposed() {
+ if (disposed) {
+ throw new DisposedException("java_remote_bridge " + this
+ + " is disposed");
+ }
+ }
+
+ private final ProxyFactory proxyFactory;
+
+ // Access to disposeListeners must be synchronized on <CODE>this</CODE>:
+ private final ArrayList<DisposeListener> disposeListeners = new ArrayList<DisposeListener>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/java/java_environment.java b/ridljar/com/sun/star/lib/uno/environments/java/java_environment.java
new file mode 100644
index 000000000..89e4b1088
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/java/java_environment.java
@@ -0,0 +1,312 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.java;
+
+import com.sun.star.uno.IEnvironment;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.UnoRuntime;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * The java_environment is the environment where objects and
+ * interfaces are registered, which are mapped out of java or
+ * into java.
+ *
+ * <p>The java_environment implements the <code>IEnvironment</code> interface
+ * defined in the uno runtime.</p>
+ *
+ * @see com.sun.star.uno.UnoRuntime
+ * @see com.sun.star.uno.IEnvironment
+ * @since UDK1.0
+ */
+public final class java_environment implements IEnvironment {
+ public java_environment(Object context) {
+ this.context = context;
+ }
+
+ /**
+ *
+ * @see com.sun.star.uno.IEnvironment#getContext
+ */
+ public Object getContext() {
+ return context;
+ }
+
+ /**
+ *
+ * @see com.sun.star.uno.IEnvironment#getName
+ */
+ public String getName() {
+ return "java";
+ }
+
+ /**
+ *
+ * @see com.sun.star.uno.IEnvironment#registerInterface
+ */
+ public Object registerInterface(Object object, String[] oid, Type type) {
+ if (oid[0] == null) {
+ oid[0] = UnoRuntime.generateOid(object);
+ }
+ return (isProxy(object) ? proxies : localObjects).register(
+ object, oid[0], type);
+ }
+
+ /**
+ * You have to revoke ANY interface that has been registered via this
+ * method.
+ *
+ * @param oid object id of interface to be revoked.
+ * @param type the type description of the interface.
+ * @see com.sun.star.uno.IEnvironment#revokeInterface
+ */
+ public void revokeInterface(String oid, Type type) {
+ if (!proxies.revoke(oid, type)) {
+ localObjects.revoke(oid, type);
+ }
+ }
+
+ /**
+ * Retrieves an interface identified by its object id and type from this
+ * environment.
+ *
+ * @param oid object id of interface to be retrieved.
+ * @param type the type description of the interface to be retrieved.
+ * @see com.sun.star.uno.IEnvironment#getRegisteredInterface
+ */
+ public Object getRegisteredInterface(String oid, Type type) {
+ Object o = proxies.get(oid, type);
+ if (o == null) {
+ o = localObjects.get(oid, type);
+ }
+ return o;
+ }
+
+ /**
+ * Retrieves the object identifier for a registered interface from this
+ * environment.
+ *
+ * @param object a registered interface.
+ * @see com.sun.star.uno.IEnvironment#getRegisteredObjectIdentifier
+ */
+ public String getRegisteredObjectIdentifier(Object object) {
+ return UnoRuntime.generateOid(object);
+ }
+
+ /**
+ *
+ * @see com.sun.star.uno.IEnvironment#list
+ */
+ public void list() {
+// TODO???
+ }
+
+ /**
+ * Revokes all registered proxy interfaces.
+ *
+ * <p>This method should be part of <code>IEnvironment</code>. It is called
+ * from <code>com.sun.star.lib.uno.bridges.java_remote.<!--
+ * -->java_remote_bridge.dispose</code>.</p>
+ */
+ public void revokeAllProxies() {
+ proxies.clear();
+ }
+
+ // TODO What's this??? java.lang.Object#equals requires reflexivity...
+ //
+ // Maybe this was hacked in so that different bridges use different
+ // instances of java_environment. That is desirable for the following
+ // reason: An OID is bridged in over bridge A, a proxy is created on the
+ // Java side, and recorded in the java_environment. The same OID is then
+ // bridged in over another bridge B. If there were only one
+ // java_environment shared by both bridges, the proxy from bridge A would be
+ // reused. If now bridge A is taken down programmatically (e.g., because
+ // some controlling code somehow deduced that no objects are mapped over
+ // that bridge any longer), but the proxy is still used by bridge B, using
+ // the proxy would now result in errors. The explicit API to control
+ // bridges forbids to transparently share proxies between bridges, and using
+ // different java_environment instances for different bridges is the way to
+ // enforce this.
+ @Override
+ public boolean equals(Object obj) {
+ return false;
+ }
+
+ private static final class Registry {
+ public synchronized Object register(
+ Object object, String oid, Type type)
+ {
+ cleanUp();
+ Level1Entry l1 = level1map.get(oid);
+ if (l1 != null) {
+ Level2Entry l2 = l1.level2map.get(type);
+ if (l2 != null) {
+ Object o = l2.get();
+ if (o != null) {
+ l2.acquire();
+ return o;
+ }
+ }
+ }
+ // TODO If a holder references an unreachable object, but still has
+ // a positive count, it is replaced with a new holder (referencing a
+ // reachable object, and with a count of 1). Any later calls to
+ // revoke that should decrement the count of the previous holder
+ // would now decrement the count of the new holder, removing it
+ // prematurely. This is a design flaw that will be fixed when
+ // IEnvironment.revokeInterface is changed to no longer use
+ // counting. (And this problem is harmless, as currently a holder
+ // either references a strongly held object and uses register/revoke
+ // to control it, or references a weakly held proxy and never
+ // revokes it.)
+ if (l1 == null) {
+ l1 = new Level1Entry();
+ level1map.put(oid, l1);
+ }
+ l1.level2map.put(type, new Level2Entry(oid, type, object, queue));
+ return object;
+ }
+
+ public synchronized boolean revoke(String oid, Type type) {
+ Level1Entry l1 = level1map.get(oid);
+ Level2Entry l2 = null;
+ if (l1 != null) {
+ l2 = l1.level2map.get(type);
+ if (l2 != null && l2.release()) {
+ removeLevel2Entry(l1, oid, type);
+ }
+ }
+ cleanUp();
+ return l2 != null;
+ }
+
+ public synchronized Object get(String oid, Type type) {
+ Level1Entry l1 = level1map.get(oid);
+ return l1 == null ? null : l1.find(type);
+ }
+
+ public synchronized void clear() {
+ level1map.clear();
+ cleanUp();
+ }
+
+ // must only be called while synchronized on this Registry:
+ private void cleanUp() {
+ for (;;) {
+ Object tmp = queue.poll();
+ Level2Entry l2 = (Level2Entry) tmp;
+ if (l2 == null) {
+ break;
+ }
+ // It is possible that a Level2Entry e1 for the OID/type pair
+ // (o,t) becomes weakly reachable, then another Level2Entry e2
+ // is registered for the same pair (o,t) (a new Level2Entry is
+ // created since now e1.get() == null), and only then e1 is
+ // enqueued. To not erroneously remove the new e2 in that case,
+ // check whether the map still contains e1:
+ Level1Entry l1 = level1map.get(l2.oid);
+ if (l1 != null && l1.level2map.get(l2.type) == l2) {
+ removeLevel2Entry(l1, l2.oid, l2.type);
+ }
+ }
+ }
+
+ // must only be called while synchronized on this Registry:
+ private void removeLevel2Entry(Level1Entry l1, String oid, Type type) {
+ l1.level2map.remove(type);
+ if (l1.level2map.isEmpty()) {
+ level1map.remove(oid);
+ }
+ }
+
+ private static final class Level1Entry {
+ // must only be called while synchronized on enclosing Registry:
+ public Object find(Type type) {
+ // First, look for an exactly matching entry; then, look for an
+ // arbitrary entry for a subtype of the request type:
+ Level2Entry l2 = level2map.get(type);
+ if (l2 != null) {
+ Object o = l2.get();
+ if (o != null) {
+ return o;
+ }
+ }
+ for (Iterator<Level2Entry> i = level2map.values().iterator();
+ i.hasNext();)
+ {
+ l2 = i.next();
+ if (type.isSupertypeOf(l2.type)) {
+ Object o = l2.get();
+ if (o != null) {
+ return o;
+ }
+ }
+ }
+ return null;
+ }
+
+ public final HashMap<Type, Level2Entry> level2map =
+ new HashMap<Type, Level2Entry>();
+ }
+
+ private static final class Level2Entry extends WeakReference<Object> {
+ public Level2Entry(
+ String oid, Type type, Object object, ReferenceQueue<Object> queue)
+ {
+ super(object, queue);
+ this.oid = oid;
+ this.type = type;
+ }
+
+ // must only be called while synchronized on enclosing Registry:
+ public void acquire() {
+ ++count;
+ }
+
+ // must only be called while synchronized on enclosing Registry:
+ public boolean release() {
+ return --count == 0;
+ }
+
+ public final String oid;
+ public final Type type;
+
+ private int count = 1;
+ }
+
+ private final HashMap<String, Level1Entry> level1map =
+ new HashMap<String, Level1Entry>();
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ }
+
+ private boolean isProxy(Object object) {
+ return object instanceof com.sun.star.lib.uno.Proxy;
+ }
+
+ private static final Registry localObjects = new Registry();
+
+ private final Object context;
+ private final Registry proxies = new Registry();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java b/ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java
new file mode 100644
index 000000000..c2ecbf9a0
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java
@@ -0,0 +1,93 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import java.io.IOException;
+
+/**
+ * An abstraction of remote bridge protocols.
+ *
+ * <p>A class implementing a given protocol <var>prot</var> must be named
+ * <code>com.sun.star.lib.uno.protocols.<var>prot</var>.<var>prot</var></code>
+ * and must have a public constructor that takes four arguments: The first
+ * argument of type <code>com.sun.star.uno.IBridge</code> must not be null. The
+ * second argument of type <code>String</code> represents any attributes; it may
+ * be null if there are no attributes. The third argument of type
+ * <code>java.io.InputStream</code> must not be null. The fourth argument of
+ * type <code>java.io.OutputStream</code> must not be null.</p>
+ */
+public interface IProtocol {
+ /**
+ * Initializes the connection.
+ *
+ * <p>This method must be called exactly once, after the
+ * <code>readMessage</code> loop has already been established.</p>
+ */
+ void init() throws IOException;
+
+ void terminate();
+
+ /**
+ * Reads a request or reply message.
+ *
+ * <p>Access to this method from multiple threads must be properly
+ * synchronized.</p>
+ *
+ * @return a non-null message; if the input stream is exhausted, a
+ * <code>java.io.IOException</code> is thrown instead.
+ */
+ Message readMessage() throws IOException;
+
+ /**
+ * Writes a request message.
+ *
+ * @param oid a non-null OID.
+ * @param type a non-null UNO type.
+ * @param function a non-null function (the name of a UNO interface method
+ * or attribute compatible with the given <code>type</code>, or either
+ * <code>"queryInterface"</code> or <code>"release"</code>).
+ * @param tid a non-null TID.
+ * @param arguments a list of UNO arguments compatible with the given
+ * <code>type</code> and <code>function</code>; may be null to represent
+ * an empty list.
+ * @return <code>true</code> if the request message is sent as a synchronous
+ * request.
+ */
+ boolean writeRequest(
+ String oid, TypeDescription type, String function, ThreadId tid,
+ Object[] arguments)
+ throws IOException;
+
+ /**
+ * Writes a reply message.
+ *
+ * @param exception <code>true</code> if the reply corresponds to a raised
+ * exception.
+ * @param tid a non-null TID.
+ * @param result if <code>exception</code> is <code>true</code>, a non-null
+ * UNO exception; otherwise, a UNO return value, which may be null to
+ * represent a <code>VOID</code> return value.
+ */
+ void writeReply(boolean exception, ThreadId tid, Object result)
+ throws IOException;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java b/ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java
new file mode 100644
index 000000000..e39ae3d4d
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java
@@ -0,0 +1,40 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+/**
+ * An abstraction for giving back a reply for a request.
+ *
+ * @see com.sun.star.uno.IQueryInterface
+ */
+public interface IReceiver {
+ /**
+ * Send back a reply for a request.
+ *
+ * @param exception <CODE>true</CODE> if an exception (instead of a normal
+ * result) is sent back.
+ * @param threadId the thread ID of the request.
+ * @param result the result of executing the request, or an exception thrown
+ * while executing the request.
+ */
+ void sendReply(boolean exception, ThreadId threadId, Object result);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java b/ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java
new file mode 100644
index 000000000..1de31ad04
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java
@@ -0,0 +1,115 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+/**
+ * This interface is an abstraction of the various threadpool implementations.
+ *
+ * @see com.sun.star.lib.uno.environments.remote.ThreadPoolManager
+ * @since UDK1.0
+ */
+public interface IThreadPool {
+ /**
+ * Retrieves the global threadId for the current thread.
+ *
+ * @return the thread id.
+ */
+ ThreadId getThreadId();
+
+ /**
+ * Attaches this thread to the thread pool.
+ *
+ * @see #enter
+ */
+ void attach();
+
+ /**
+ * As above, but hands in an already existing instance of the threadid of
+ * the current thread.
+ *
+ * <p>The function exists for performance.</p>
+ *
+ * @return Returns a handle which can be used in enter and detach calls.
+ * @see #attach
+ */
+ Object attach( ThreadId id );
+
+ /**
+ * Detaches this thread from the thread pool.
+ * @see #enter
+ */
+ void detach();
+
+ /**
+ * As above, but hands in an already existing instance of the threadid of
+ * the current thread and a handle returned by attach.
+ *
+ * <p>The function exists for performance.</p>
+ *
+ * @see #attach()
+ * @see #detach()
+ */
+ void detach( Object handle, ThreadId id );
+
+ /**
+ * Lets this thread enter the thread pool.
+ *
+ * <p>This thread then executes all jobs put via <code>putJob</code> until
+ * a reply job arrives.</p>
+ *
+ * @see #putJob
+ */
+ Object enter() throws Throwable;
+
+ /**
+ * As above but hands in an already existing instance of the threadid of
+ * the current thread and a handle returned by attach.
+ *
+ * <p>This thread then executes all jobs put via <code>putJob</code> until
+ * a reply job arrives.</p>
+ *
+ * @see #putJob
+ */
+ Object enter( Object handle, ThreadId id ) throws Throwable;
+
+ /**
+ * Queues a job into the jobQueue of the thread belonging to the jobs
+ * threadId.
+ *
+ * @param job the job
+ */
+ void putJob(Job job);
+
+ /**
+ * Disposes this thread pool, thus releasing all threads by throwing a
+ * <code>DisposedException</code> with the given <code>Throwable</code> cause.
+ *
+ * @param throwable the cause
+ */
+ void dispose(Throwable throwable);
+
+
+ /**
+ * Destroys the thread pool and tries to join all created threads immediately.
+ */
+ void destroy();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java
new file mode 100644
index 000000000..332306be0
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java
@@ -0,0 +1,124 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+/**
+ * This class implements a java thread pool.
+ *
+ * @see com.sun.star.uno.UnoRuntime
+ * @see com.sun.star.lib.uno.environments.remote.NativeThreadPool
+ * @see com.sun.star.lib.uno.environments.remote.IThreadPool
+ * @see com.sun.star.lib.uno.environments.remote.Job
+ * @see com.sun.star.lib.uno.environments.remote.JobQueue
+ * @since UDK1.0
+ */
+public class JavaThreadPool implements IThreadPool {
+ /**
+ * When set to true, enables various debugging output.
+ */
+ private static final boolean DEBUG = false;
+
+ JavaThreadPoolFactory _javaThreadPoolFactory;
+
+ JavaThreadPool(JavaThreadPoolFactory javaThreadPoolFactory) {
+ _javaThreadPoolFactory = javaThreadPoolFactory;
+ }
+
+ public ThreadId getThreadId() {
+ return JavaThreadPoolFactory.getThreadId();
+ }
+
+ public Object attach( ThreadId threadId )
+ {
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".attach - id:" + threadId);
+ JobQueue jobQueue = _javaThreadPoolFactory.getJobQueue(threadId);
+ if(jobQueue == null)
+ jobQueue = new JobQueue(_javaThreadPoolFactory, threadId, false);
+
+ // acquiring the jobQueue registers it at the ThreadPoolFactory
+ jobQueue.acquire();
+ return jobQueue;
+ }
+
+ public void attach() {
+ attach( getThreadId() );
+ }
+
+ public void detach( Object handle, ThreadId id )
+ {
+ ((JobQueue)handle).release();
+ }
+
+ public void detach() {
+ ThreadId threadId = getThreadId();
+ detach(_javaThreadPoolFactory.getJobQueue(threadId), threadId );
+ }
+
+
+ public Object enter( ) throws Throwable {
+ ThreadId threadId = getThreadId();
+ return enter( _javaThreadPoolFactory.getJobQueue( threadId ), threadId );
+ }
+
+ public Object enter( Object handle, ThreadId threadId ) throws Throwable {
+ return ((JobQueue)handle).enter(this);
+ }
+
+ public void putJob(Job job) {
+ if (!job.isRequest() || job.isSynchronous()) {
+ JobQueue jobQueue = _javaThreadPoolFactory.getJobQueue(job.getThreadId());
+
+ // this has not be synchronized, cause
+ // sync jobs can only come over one bridge
+ // (cause the thread blocks on other side)
+ if(jobQueue == null)
+ jobQueue = new JobQueue(_javaThreadPoolFactory, job.getThreadId(), true);
+
+ // put job acquires the queue and registers it at the ThreadPoolFactory
+ jobQueue.putJob(job, this);
+ }
+ else {
+ // this has to be synchronized, cause
+ // async jobs of the same thread can come
+ // over different bridges
+ synchronized(_javaThreadPoolFactory) {
+ JobQueue async_jobQueue = _javaThreadPoolFactory.getAsyncJobQueue(job.getThreadId());
+
+ // ensure there is jobQueue
+ if(async_jobQueue == null) // so, there is really no async queue
+ async_jobQueue = new JobQueue(_javaThreadPoolFactory, job.getThreadId());
+
+ // put job acquires the queue and registers it at the ThreadPoolFactory
+ async_jobQueue.putJob(job, this);
+ }
+ }
+ }
+
+ public void dispose(Throwable throwable) {
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".dispose:" + throwable);
+
+ _javaThreadPoolFactory.dispose(this, throwable);
+ }
+
+ public void destroy() {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java
new file mode 100644
index 000000000..181a3e17e
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java
@@ -0,0 +1,87 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.WeakHashMap;
+
+final class JavaThreadPoolFactory {
+
+ public IThreadPool createThreadPool() {
+ return new JavaThreadPool(this);
+ }
+
+ public void addJobQueue(JobQueue jobQueue) {
+ synchronized (jobQueues) {
+ jobQueues.put(jobQueue.getThreadId(), jobQueue);
+ }
+ }
+
+ public void removeJobQueue(JobQueue jobQueue) {
+ synchronized (jobQueues) {
+ jobQueues.remove(jobQueue.getThreadId());
+ }
+ }
+
+ public JobQueue getJobQueue(ThreadId threadId) {
+ synchronized (jobQueues) {
+ return jobQueues.get(threadId);
+ }
+ }
+
+ public JobQueue getAsyncJobQueue(ThreadId threadId) {
+ JobQueue q = getJobQueue(threadId);
+ return q == null ? null : q._async_jobQueue;
+ }
+
+ public void dispose(Object disposeId, Throwable throwable) {
+ JobQueue[] qs;
+ synchronized (jobQueues) {
+ Collection<JobQueue> c = jobQueues.values();
+ qs = c.toArray(new JobQueue[c.size()]);
+ }
+ for (int i = 0; i < qs.length; ++i) {
+ qs[i].dispose(disposeId, throwable);
+ }
+ }
+
+ public static ThreadId getThreadId() {
+ Thread t = Thread.currentThread();
+ if (t instanceof JobQueue.JobDispatcher) {
+ return ((JobQueue.JobDispatcher) t).getThreadId();
+ } else {
+ ThreadId id;
+ synchronized (threadIdMap) {
+ id = threadIdMap.get(t);
+ if (id == null) {
+ id = ThreadId.createFresh();
+ threadIdMap.put(t, id);
+ }
+ }
+ return id;
+ }
+ }
+
+ private static final WeakHashMap<Thread, ThreadId> threadIdMap = new WeakHashMap<Thread, ThreadId>();
+ private final HashMap<ThreadId, JobQueue> jobQueues = new HashMap<ThreadId, JobQueue>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/Job.java b/ridljar/com/sun/star/lib/uno/environments/remote/Job.java
new file mode 100644
index 000000000..8ec4492c4
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/Job.java
@@ -0,0 +1,163 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import java.lang.reflect.InvocationTargetException;
+
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XCurrentContext;
+
+/**
+ * The Job is an abstraction for tasks which have to be done
+ * remotely because of a method invocation.
+ *
+ * @see com.sun.star.lib.uno.environments.remote.ThreadId
+ * @see com.sun.star.lib.uno.environments.remote.IReceiver
+ * @since UDK1.0
+ */
+public class Job {
+ protected IReceiver _iReceiver;
+ protected Message _iMessage;
+ Object _disposeId;
+
+ protected Object _object;
+
+ public Job(Object object, IReceiver iReceiver, Message iMessage) {
+ _object = object;
+ _iReceiver = iReceiver;
+ _iMessage = iMessage;
+ }
+
+ /**
+ * Dispatches a <code>queryInterface</code> call.
+ *
+ * @return the result of the call (should be an <code>Any</code>).
+ */
+ protected Object dispatch_queryInterface(Type type) {
+ Class<?> zInterface = type.getTypeDescription().getZClass();
+
+ Object result = null;
+
+ Object face = UnoRuntime.queryInterface(zInterface, _object);
+ // the hell knows why, but empty interfaces a given back as void anys
+ if(face != null)
+ result = new Any(type, face);
+ return result;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return the result of the message.
+ */
+ public Object execute() throws Throwable {
+ if (_iMessage.isRequest()) {
+ Object result = null;
+ Throwable exception = null;
+ MethodDescription md = _iMessage.getMethod();
+ Object[] args = _iMessage.getArguments();
+ XCurrentContext oldCC = UnoRuntime.getCurrentContext();
+ UnoRuntime.setCurrentContext(_iMessage.getCurrentContext());
+ try {
+ result = md.getIndex() == MethodDescription.ID_QUERY_INTERFACE
+ ? dispatch_queryInterface((Type) args[0])
+ : md.getMethod().invoke(_object, args);
+ } catch (InvocationTargetException e) {
+ exception = e.getCause();
+ if (exception == null) {
+ exception = e;
+ }
+ } catch (Exception e) {
+ exception = e;
+ } finally {
+ UnoRuntime.setCurrentContext(oldCC);
+ }
+ if (_iMessage.isSynchronous()) {
+ if (exception == null) {
+ _iReceiver.sendReply(
+ false, _iMessage.getThreadId(), result);
+ } else {
+ // Here we have to be aware of non-UNO exceptions, because
+ // they may kill a remote side which does not know anything
+ // about their types:
+ if (!(exception instanceof com.sun.star.uno.Exception)
+ && !(exception instanceof
+ com.sun.star.uno.RuntimeException))
+ {
+ StringWriter writer = new StringWriter();
+ exception.printStackTrace(new PrintWriter(writer));
+ exception = new com.sun.star.uno.RuntimeException(
+ "Java exception: <" + writer + ">", null);
+ }
+ _iReceiver.sendReply(
+ true, _iMessage.getThreadId(), exception);
+ }
+ }
+ return null;
+ } else if (_iMessage.isAbnormalTermination()) {
+ throw remoteUnoRequestRaisedException(_iMessage.getResult());
+ } else {
+ return _iMessage.getResult();
+ }
+ }
+
+ public ThreadId getThreadId() {
+ return _iMessage.getThreadId();
+ }
+
+ public boolean isRequest() {
+ return _iMessage.isRequest();
+ }
+
+ public boolean isSynchronous() {
+ return _iMessage.isSynchronous();
+ }
+
+ public void dispose() {
+// _oId = null;
+// _iReceiver = null;
+// _threadId = null;
+// _object = null;
+// _operation = null;
+// _param = null;
+// _exception = null;
+// _zInterface = null;
+// _disposeId = null;
+ }
+
+ /**
+ * The name of this method is chosen to generate a somewhat self-explanatory
+ * stack trace.
+ */
+ private Exception remoteUnoRequestRaisedException(Object exception) {
+ Exception e = (Exception) exception;
+ e.fillInStackTrace();
+ return e;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java b/ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java
new file mode 100644
index 000000000..b71525ba5
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java
@@ -0,0 +1,373 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+import java.util.ArrayList;
+
+import com.sun.star.lang.DisposedException;
+
+/**
+ * The <code>JobQueue</code> implements a queue for jobs.
+ *
+ * <p>For every jobs thread id exists a job queue which is registered
+ * at the <code>ThreadPool</code>.</p>
+ *
+ * <p>A JobQueue is split into a sync job queue and an async job queue.
+ * The sync job queue is the registered queue, it delegates async jobs
+ * (put by <code>putjob</code>) into the async queue, which is only
+ * known by the sync queue.</p>
+ *
+ * @see com.sun.star.lib.uno.environments.remote.IThreadPool
+ * @see com.sun.star.lib.uno.environments.remote.Job
+ * @see com.sun.star.lib.uno.environments.remote.ThreadId
+ * @since UDK1.0
+ */
+public class JobQueue {
+ /**
+ * When set to true, enables various debugging output.
+ */
+ private static final boolean DEBUG = false;
+
+ final ArrayList<Job> jobList = new ArrayList<Job>();
+
+ private ThreadId _threadId; // the thread id of the queue
+ protected int _ref_count = 0; // the stack deepness
+ private boolean _createThread; // create a worker thread, if needed
+ private boolean _createThread_now; // create a worker thread, if needed
+ private Thread _worker_thread; // the thread that does the jobs
+
+ private Object _disposeId; // the active dispose id
+ private Object _doDispose = null;
+ private Throwable _throwable;
+
+ JobQueue _async_jobQueue; // chaining job queues for asyncs
+ protected JobQueue _sync_jobQueue; // chaining job queues for syncs
+
+ private boolean _active = false;
+
+ private JavaThreadPoolFactory _javaThreadPoolFactory;
+
+ /**
+ * A thread for dispatching jobs.
+ */
+ class JobDispatcher extends Thread {
+ Object _disposeId;
+
+ JobDispatcher(Object disposeId) {
+ super("JobDispatcher");
+
+ if(DEBUG) System.err.println("JobQueue$JobDispatcher.<init>:" + _threadId);
+
+ _disposeId = disposeId;
+ }
+
+ ThreadId getThreadId() {
+ return _threadId;
+ }
+
+ @Override
+ public void run() {
+ if(DEBUG) System.err.println("ThreadPool$JobDispatcher.run: " + Thread.currentThread());
+
+ try {
+ enter(2000, _disposeId);
+ } catch(Throwable throwable) {
+ synchronized (JobQueue.this) {
+ if(!jobList.isEmpty() || _active) { // there was a job in progress, so give a stack
+ System.err.println(getClass().getName() + " - exception occurred:" + throwable);
+ throwable.printStackTrace(System.err);
+ }
+ }
+ }
+ finally {
+ release();
+ }
+
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".run - exit:" + _threadId);
+ }
+ }
+
+
+ /**
+ * Constructs an async job queue with the given thread id which belongs to
+ * the given sync job queue.
+ *
+ * @param threadId the thread id.
+ * @see com.sun.star.lib.uno.environments.remote.ThreadId
+ */
+ JobQueue(JavaThreadPoolFactory javaThreadPoolFactory, ThreadId threadId) {
+ _javaThreadPoolFactory = javaThreadPoolFactory;
+ _threadId = ThreadId.createFresh();
+
+ _sync_jobQueue = javaThreadPoolFactory.getJobQueue(threadId);
+ if(_sync_jobQueue == null) {
+ _sync_jobQueue = new JobQueue(javaThreadPoolFactory, threadId, true);
+ _sync_jobQueue.acquire();
+ }
+
+ _sync_jobQueue._async_jobQueue = this;
+
+ _createThread = true;
+ _createThread_now = true;
+
+ acquire();
+
+ if(DEBUG) System.err.println("##### " + getClass().getName() + " - init:" + _threadId);
+ }
+
+ /**
+ * Constructs a sync job queue with the given thread id and the given thread.
+ *
+ * @param threadId the thread id.
+ * @param createThread if true, the queue creates a worker thread if needed.
+ * @see com.sun.star.lib.uno.environments.remote.ThreadId
+ */
+ JobQueue(JavaThreadPoolFactory javaThreadPoolFactory, ThreadId threadId, boolean createThread){
+ _javaThreadPoolFactory = javaThreadPoolFactory;
+ _threadId = threadId;
+ _createThread = createThread;
+ _createThread_now = createThread;
+
+ if(DEBUG) System.err.println("##### " + getClass().getName() + " - init:" + _threadId + " " + createThread);
+ }
+
+ /**
+ * Gives the thread id of this queue.
+ *
+ * @return the thread id.
+ * @see com.sun.star.lib.uno.environments.remote.ThreadId
+ */
+ ThreadId getThreadId() {
+ return _threadId;
+ }
+
+ synchronized void acquire() {
+ // add only synchronous queues .
+ if(_ref_count <= 0 && _sync_jobQueue == null )
+ _javaThreadPoolFactory.addJobQueue(this);
+
+ ++ _ref_count;
+ }
+
+ synchronized void release() {
+ -- _ref_count;
+
+ if(_ref_count <= 0) {
+ // only synchronous queues needs to be removed .
+ if( _sync_jobQueue == null )
+ _javaThreadPoolFactory.removeJobQueue(this);
+
+
+ if(_sync_jobQueue != null) {
+ _sync_jobQueue._async_jobQueue = null;
+ _sync_jobQueue.release();
+ }
+ }
+ }
+
+ /**
+ * Removes a job from the queue.
+ *
+ * @param waitTime the maximum amount of time to wait for a job.
+ * @return a job or null if timed out.
+ */
+ private Job removeJob(int waitTime) {
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".removeJob:" + jobList + " " + _threadId);
+
+ Job job = null;
+ synchronized (this) {
+ // wait max. waitTime time for a job to enter the queue
+ boolean waited = false;
+ while(jobList.isEmpty() && (waitTime == 0 || !waited)) {
+ if(_doDispose == _disposeId) {
+ _doDispose = null;
+ throw (DisposedException)
+ new DisposedException().initCause(_throwable);
+ }
+
+ // notify sync queues
+ notifyAll();
+
+ try {
+ // wait for new job
+ wait(waitTime);
+ } catch(InterruptedException interruptedException) {
+ throw new com.sun.star.uno.RuntimeException(getClass().getName() + ".removeJob - unexpected:" + interruptedException);
+ }
+
+ // signal that we have already waited once
+ waited = true;
+ }
+
+
+ if(!jobList.isEmpty()) {
+ job = jobList.remove(0);
+ _active = true;
+ }
+ }
+
+ // always wait for asynchron jobqueue to be finished !
+ if(job != null && _async_jobQueue != null) {
+ synchronized(_async_jobQueue) {
+ // wait for async queue to be empty and last job to be done
+ while(_async_jobQueue._active || !_async_jobQueue.jobList.isEmpty()) {
+ if(DEBUG) System.err.println("waiting for async:" + _async_jobQueue.jobList + " " + _async_jobQueue._worker_thread);
+
+ if(_doDispose == _disposeId) {
+ _doDispose = null;
+ throw (DisposedException)
+ new DisposedException().initCause(_throwable);
+ }
+
+ try {
+ _async_jobQueue.wait();
+ } catch(InterruptedException interruptedException) {
+ throw new com.sun.star.uno.RuntimeException(getClass().getName() + ".removeJob - unexpected:" + interruptedException);
+ }
+ }
+ }
+ }
+
+ return job;
+ }
+
+ /**
+ * Puts a job into the queue.
+ *
+ * @param job the job.
+ * @param disposeId a dispose id.
+ */
+ synchronized void putJob(Job job, Object disposeId) {
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".putJob todoes: " + " job:" + job);
+
+ jobList.add(job);
+
+ if(_worker_thread == null && _createThread && _createThread_now) { // if there is no thread, which dispatches and if shall create one, create one
+
+ acquire();
+
+ _createThread_now = false;
+ new JobDispatcher(disposeId).start();
+ }
+
+ // always notify possible waiters
+ notifyAll();
+ }
+
+ /**
+ * Enters the job queue.
+ *
+ * @param disposeId a dispose id.
+ * @return the result of the final job (reply).
+ */
+ Object enter(Object disposeId) throws Throwable {
+ return enter(0, disposeId); // wait infinitely
+ }
+
+ /**
+ * Enters the job queue.
+ *
+ * @param waitTime the maximum amount of time to wait for a job (0 means wait infinitely).
+ * @param disposeId a dispose id.
+ * @return the result of the final job (reply).
+ */
+ Object enter(int waitTime, Object disposeId) throws Throwable {
+ if(DEBUG) System.err.println("#####" + getClass().getName() + ".enter: " + _threadId);
+
+ boolean quit = false;
+
+ Object hold_disposeId = _disposeId;
+ _disposeId = disposeId;
+
+ Object result = null;
+
+ Thread hold_worker_thread = _worker_thread;
+ _worker_thread = Thread.currentThread();
+
+ while(!quit) {
+ Job job = null;
+
+ try {
+ job = removeJob(waitTime);
+
+ if(job != null) {
+ try {
+ result = job.execute();
+ }
+ finally {
+ _active = false;
+ }
+
+ if (!job.isRequest()) {
+ job.dispose();
+
+ quit = true;
+ }
+
+ job = null;
+ }
+ else
+ quit = true;
+
+
+ }
+ finally { // ensure that this queue becomes disposed, if necessary
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".enter leaving: " + _threadId + " " + _worker_thread + " " + hold_worker_thread + " " + result);
+
+ synchronized(this) {
+ if(job != null || (quit && jobList.isEmpty())) {
+ _worker_thread = hold_worker_thread;
+
+ _createThread_now = true;
+
+ _disposeId = hold_disposeId;
+
+ if(_sync_jobQueue != null)
+ notifyAll(); // notify waiters (e.g. this is an asyncQueue and there is a sync waiting)
+ }
+ else
+ quit = false;
+
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * If the given disposeId is registered, interrupts the worker thread.
+ *
+ * @param disposeId the dispose id.
+ */
+ synchronized void dispose(Object disposeId, Throwable throwable) {
+ if(_sync_jobQueue == null) { // dispose only sync queues
+ _doDispose = disposeId;
+ _throwable = throwable;
+
+ // get thread out of wait and let it throw the throwable
+ if(DEBUG) System.err.println(getClass().getName() + ".dispose - notifying thread");
+
+ notifyAll();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/Message.java b/ridljar/com/sun/star/lib/uno/environments/remote/Message.java
new file mode 100644
index 000000000..b34295ae3
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/Message.java
@@ -0,0 +1,189 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.uno.XCurrentContext;
+
+/**
+ * A remote request or reply message.
+ */
+public class Message {
+ public Message(
+ ThreadId threadId, boolean request, String objectId,
+ TypeDescription type, MethodDescription method, boolean synchronous,
+ XCurrentContext currentContext, boolean abnormalTermination,
+ Object result, Object[] arguments)
+ {
+ this.threadId = threadId;
+ this.request = request;
+ this.objectId = objectId;
+ this.type = type;
+ this.method = method;
+ this.synchronous = synchronous;
+ this.currentContext = currentContext;
+ this.abnormalTermination = abnormalTermination;
+ this.result = result;
+ this.arguments = arguments;
+ }
+
+ /**
+ * Returns the thread ID of the message.
+ *
+ * <p>Valid for all kinds of messages.</p>
+ *
+ * @return the (non-<code>null</code>) thread ID.
+ */
+ public final ThreadId getThreadId() {
+ return threadId;
+ }
+
+ /**
+ * Returns whether the message is a request or a reply.
+ *
+ * <p>Valid for all kinds of messages.</p>
+ *
+ * @return <code>true</code> for a request, <code>false</code> for a reply.
+ */
+ public final boolean isRequest() {
+ return request;
+ }
+
+ /**
+ * Returns the object ID of a request message.
+ *
+ * <p>Valid only for request messages.</p>
+ *
+ * @return the (non-<code>null</code>) object ID for a request,
+ * <code>null</code> for a reply.
+ */
+ public final String getObjectId() {
+ return objectId;
+ }
+
+ /**
+ * Returns the type of a request message.
+ *
+ * <p>Valid only for request messages.</p>
+ *
+ * @return the (non-<code>null</code>) type for a request, <code>null</code>
+ * for a reply.
+ */
+ public final TypeDescription getType() {
+ return type;
+ }
+
+ /**
+ * Returns the method description of a request message.
+ *
+ * <p>Valid only for request messages. The returned
+ * <code>MethodDescription</code> is consistent with the type of the
+ * message.</p>
+ *
+ * @return the (non-<code>null</code>) method description for a request,
+ * <code>null</code> for a reply.
+ */
+ public final MethodDescription getMethod() {
+ return method;
+ }
+
+ /**
+ * Returns whether the request message is synchronous.
+ *
+ * <p>Valid only for request messages.</p>
+ *
+ * @return <code>true</code> for a synchronous request, <code>false</code>
+ * for an asynchronous request or a reply.
+ */
+ public final boolean isSynchronous() {
+ return synchronous;
+ }
+
+ /**
+ * Returns the current context of a request message.
+ *
+ * <p>Valid only for request messages.</p>
+ *
+ * @return the current context (which may be <code>null</code>) for a
+ * request, <code>null</code> for a reply.
+ */
+ public XCurrentContext getCurrentContext() {
+ return currentContext;
+ }
+
+ /**
+ * Returns whether the reply message represents abnormal termination.
+ *
+ * <p>Valid only for reply messages.</p>
+ *
+ * @return <code>true</code> for a reply that represents abnormal
+ * termination, <code>false</code> for a reply that represents normal
+ * termination or a request.
+ */
+ public final boolean isAbnormalTermination() {
+ return abnormalTermination;
+ }
+
+ /**
+ * Returns the result of a reply message.
+ *
+ * <p>Valid only for reply messages.</p>
+ *
+ * @return any (possibly <code>null</code>) return value for a reply that
+ * represents normal termination, the (non-<code>null</code>) exception for
+ * a reply that represents abnormal termination, <code>null</code> for a
+ * request.
+ */
+ public final Object getResult() {
+ return result;
+ }
+
+ /**
+ * Returns the arguments of a message.
+ *
+ * <p>Valid only for request messages and reply messages that represent
+ * normal termination. Any returned array must not be modified.</p>
+ *
+ * @return the in and in&ndash; {
+ * }out arguments for a request (possibly
+ * <code>null</code> for a parameterless function), the out and in&ndash; {
+ * }out
+ * arguments for a reply that represents normal termination (possibly
+ * <code>null</code> for a parameterless function), <code>null</code> for a
+ * reply that represents abnormal termination.
+ */
+ public final Object[] getArguments() {
+ return arguments;
+ }
+
+ private final ThreadId threadId;
+ private final boolean request;
+ private final String objectId;
+ private final TypeDescription type;
+ private final MethodDescription method;
+ private final boolean synchronous;
+ private final XCurrentContext currentContext;
+ private final boolean abnormalTermination;
+ private final Object result;
+ private final Object[] arguments;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java b/ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java
new file mode 100644
index 000000000..f77bbb466
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java
@@ -0,0 +1,94 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+final class NativeThreadPool implements IThreadPool {
+ public NativeThreadPool() {
+ pool = create();
+ }
+
+ public ThreadId getThreadId() {
+ return new ThreadId(threadId());
+ }
+
+ public void attach() {
+ attach(pool);
+ }
+
+ public Object attach(ThreadId id) {
+ attach();
+ return null;
+ }
+
+ public void detach() {
+ detach(pool);
+ }
+
+ public void detach(Object handle, ThreadId id) {
+ detach();
+ }
+
+ public Object enter() throws Throwable {
+ Job job = enter(pool);
+ if (job == null) {
+ throw dispose;
+ }
+ return job.execute();
+ }
+
+ public Object enter(Object handle, ThreadId id) throws Throwable {
+ return enter();
+ }
+
+ public void putJob(Job job) {
+ putJob(
+ pool, job.getThreadId().getBytes(), job, job.isRequest(),
+ job.isRequest() && !job.isSynchronous());
+ }
+
+ public void dispose(Throwable throwable) {
+ dispose = throwable;
+ dispose(pool);
+ }
+
+ public void destroy() {
+ destroy(pool);
+ }
+
+ // The native implementation is in
+ // bridges/source/jni_uno/nativethreadpool.cxx:
+ static {
+ System.loadLibrary("java_uno");
+ }
+ private static native byte[] threadId();
+ private static native long create();
+ private static native void attach(long pool);
+ private static native Job enter(long pool);
+ private static native void detach(long pool);
+ private static native void putJob(
+ long pool, byte[] threadId, Job job, boolean request, boolean oneWay);
+ private static native void dispose(long pool);
+ private static native void destroy(long pool);
+
+ private final long pool;
+ private volatile Throwable dispose;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java
new file mode 100644
index 000000000..f8dacc78c
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java
@@ -0,0 +1,109 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.sun.star.uno.UnoRuntime;
+
+public final class ThreadId {
+ public static ThreadId createFresh() {
+ long c = count.getAndIncrement();
+ try {
+ return new ThreadId((PREFIX + c).getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("this cannot happen: " + e);
+ }
+ }
+
+ public ThreadId(byte[] id) {
+ this.id = id;
+ }
+
+ /**
+ * Indicates whether some other object is equal to this one.
+ *
+ * @param obj the reference object with which to compare.
+ * @return <code>true</code> if this object is the same as the obj argument;
+ * <code>false</code> otherwise.
+ *
+ * @see java.lang.Object#equals
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof ThreadId
+ && Arrays.equals(id, ((ThreadId) obj).id);
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return a hash code value for this object.
+ * @see java.lang.Object#hashCode
+ */
+ @Override
+ public int hashCode() {
+ int h = hash;
+ if (h == 0) {
+ // Same algorithm as java.util.List.hashCode (also see Java 1.5
+ // java.util.Arrays.hashCode(byte[])):
+ h = 1;
+ for (int i = 0; i < id.length; ++i) {
+ h = 31 * h + id[i];
+ }
+ hash = h;
+ }
+ return h;
+ }
+
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return a string representation of the object.
+ * @see java.lang.Object#toString
+ */
+ @Override
+ public String toString() {
+ StringBuffer b = new StringBuffer("[ThreadId:");
+ for (int i = 0; i < id.length; ++i) {
+ String n = Integer.toHexString(id[i] & 0xFF);
+ if (n.length() == 1) {
+ b.append('0');
+ }
+ b.append(n);
+ }
+ b.append(']');
+ return b.toString();
+ }
+
+ public byte[] getBytes() {
+ return id;
+ }
+
+ private static final String PREFIX = "java:" + UnoRuntime.getUniqueKey() + ":";
+ private static final AtomicLong count = new AtomicLong(0);
+
+ private final byte[] id;
+ private int hash = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java
new file mode 100644
index 000000000..2014f5167
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java
@@ -0,0 +1,74 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+/**
+ * Manages the UNO thread pool factory.
+ *
+ * <P>The thread pool factory is a process-wide resource. It is important that
+ * all UNO environments within a process share the same thread pool mechanisms:
+ * if a synchronous UNO call is bridged out from one local UNO environment over
+ * one remote bridge, and recursively calls back into another local UNO
+ * environment over another remote bridge, the code in the second environment
+ * should be executed in the thread that did the original call from the first
+ * environment.</P>
+ *
+ * <P>There are both a Java and a native thread pool factory. A pure Java
+ * process will always use the Java thread pool factory. A mixed process uses
+ * the system property <CODE>org.openoffice.native</CODE> (to be set by the
+ * native code that starts the JVM) to determine which implementation
+ * to use.</P>
+ */
+public final class ThreadPoolManager {
+ /**
+ * Creates a thread pool instance.
+ *
+ * @return a new thread pool instance; will never be <CODE>null</CODE>.
+ */
+ public static synchronized IThreadPool create() {
+ if (useNative) {
+ return new NativeThreadPool();
+ } else {
+ if (javaFactory == null) {
+ javaFactory = new JavaThreadPoolFactory();
+ }
+ return javaFactory.createThreadPool();
+ }
+ }
+
+ /**
+ * Leads to using the native thread pool factory, unless a Java thread pool
+ * has already been created.
+ *
+ * @return <CODE>false</CODE> if a Java thread pool has already been created.
+ */
+ public static synchronized boolean useNative() {
+ useNative = javaFactory == null;
+ return useNative;
+ }
+
+ private static boolean useNative
+ = System.getProperty("org.openoffice.native") != null;
+ private static JavaThreadPoolFactory javaFactory = null;
+
+ private ThreadPoolManager() {} // do not instantiate
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java b/ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java
new file mode 100644
index 000000000..09259c8cb
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java
@@ -0,0 +1,66 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.environments.remote;
+
+import com.sun.star.uno.IEnvironment;
+import com.sun.star.uno.Type;
+
+public final class remote_environment implements IEnvironment {
+ public remote_environment(Object context) {
+ this.context = context;
+ }
+
+ public Object getContext() {
+ return context;
+ }
+
+ public String getName() {
+ return "remote";
+ }
+
+ public Object registerInterface(Object object, String[] oid, Type type) {
+ throw new UnsupportedOperationException(
+ "java_remote environment is not functional");
+ }
+
+ public void revokeInterface(String oid, Type type) {
+ throw new UnsupportedOperationException(
+ "java_remote environment is not functional");
+ }
+
+ public Object getRegisteredInterface(String oid, Type type) {
+ throw new UnsupportedOperationException(
+ "java_remote environment is not functional");
+ }
+
+ public String getRegisteredObjectIdentifier(Object object) {
+ throw new UnsupportedOperationException(
+ "java_remote environment is not functional");
+ }
+
+ public void list() {
+ throw new UnsupportedOperationException(
+ "java_remote environment is not functional");
+ }
+
+ private final Object context;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java b/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java
new file mode 100644
index 000000000..d886ef702
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XEventListener;
+import com.sun.star.lang.EventObject;
+import com.sun.star.uno.Type;
+
+/** This class can be used as the base class for UNO components. In addition to the functionality ,which
+ * is inherited from WeakBase, it implements com.sun.star.lang.XComponent.
+ */
+public class ComponentBase extends WeakBase implements XComponent
+{
+ private static final boolean DEBUG= false;
+ protected MultiTypeInterfaceContainer listenerContainer;
+ protected boolean bInDispose= false;
+ protected boolean bDisposed= false;
+ static final Type EVT_LISTENER_TYPE= new Type(XEventListener.class);
+
+
+ /** Creates a new instance of CompBase */
+ public ComponentBase()
+ {
+ super();
+ listenerContainer= new MultiTypeInterfaceContainer();
+ }
+
+ /** Override to perform extra clean-up work. Provided for subclasses. It is
+ called during dispose()
+ */
+ protected void preDisposing()
+ {
+ }
+ /** Override to become notified right before the disposing action is performed.
+ */
+ protected void postDisposing()
+ {
+ }
+
+
+ /** Method of XComponent. It is called by the owning client when the component is not needed
+ * anymore. The registered listeners are notified that this method has been called.
+ */
+ public void dispose()
+ {
+ // Determine in a thread-safe way if this is the first call to this method.
+ // Only then we proceed with the notification of event listeners.
+ // It is an error to call this method more than once.
+ boolean bDoDispose= false;
+ synchronized (this)
+ {
+ if ( ! bInDispose && ! bDisposed)
+ {
+ bDoDispose= true;
+ bInDispose= true;
+ }
+ }
+ // The notification occurs in an unsynchronized block in order to avoid
+ // deadlocks if one of the listeners calls back in a different thread on
+ // a synchronized method which uses the same object.
+ if (bDoDispose)
+ {
+ try
+ {
+ preDisposing();
+ listenerContainer.disposeAndClear(new EventObject(this));
+ //notify subclasses that disposing is in progress
+ postDisposing();
+ }
+ finally
+ {
+ // finally makes sure that the flags are set even if a RuntimeException is thrown.
+ // That ensures that this function is only called once.
+ synchronized (this)
+ {
+ bDisposed= true;
+ bInDispose= false;
+ }
+ }
+ }
+ else
+ {
+ // in a multithreaded environment, it can't be avoided, that dispose is called twice.
+ // However this condition is traced, because it MAY indicate an error.
+ if (DEBUG)
+ System.out.println("OComponentHelper::dispose() - dispose called twice" );
+ }
+ }
+
+ /** Method of XComponent.
+ */
+ public void removeEventListener(XEventListener xEventListener)
+ {
+ listenerContainer.removeInterface( EVT_LISTENER_TYPE, xEventListener);
+ }
+
+ public void addEventListener(XEventListener listener)
+ {
+ boolean bDoDispose= false;
+ synchronized (this)
+ {
+ if (bDisposed || bInDispose)
+ bDoDispose= true;
+ else
+ listenerContainer.addInterface(EVT_LISTENER_TYPE, listener);
+ }
+ if (bDoDispose )
+ {
+ listener.disposing( new EventObject(this));
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable
+ {
+ if ( ! bInDispose && ! bDisposed)
+ dispose();
+ super.finalize();
+ }
+}
diff --git a/ridljar/com/sun/star/lib/uno/helper/Factory.java b/ridljar/com/sun/star/lib/uno/helper/Factory.java
new file mode 100644
index 000000000..056d9549d
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/Factory.java
@@ -0,0 +1,291 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.helper;
+
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.lang.XInitialization;
+import com.sun.star.registry.XRegistryKey;
+import com.sun.star.uno.UnoRuntime;
+
+
+/** Factory helper class supporting com.sun.star.lang.XServiceInfo and
+ com.sun.star.lang.XSingleComponentFactory.
+
+ <p>
+ Note: This factory implementation does not support lang.XSingleServiceFactory.
+ </p>
+*/
+public class Factory
+ extends ComponentBase
+ implements XSingleComponentFactory, XServiceInfo
+{
+ private static final boolean DEBUG = false;
+
+ /** Creates an object factory supporting interfaces
+ com.sun.star.lang.XSingleComponentFactory and
+ com.sun.star.lang.XServiceInfo
+
+ @param impl_class
+ implementation class
+ @param impl_name
+ implementation name
+ @param supported_services
+ services implemented
+ @return
+ object factory
+
+ @since UDK 3.2.13
+ */
+ public static XSingleComponentFactory createComponentFactory(
+ Class impl_class, String impl_name, String supported_services [] )
+ throws com.sun.star.uno.RuntimeException
+ {
+ return new Factory( impl_class, impl_name, supported_services );
+ }
+
+ /** Creates an object factory supporting interfaces
+ com.sun.star.lang.XSingleComponentFactory and
+ com.sun.star.lang.XServiceInfo
+
+ The implementation name is the name of the implementation class.
+
+ @param impl_class
+ implementation class
+ @param supported_services
+ services implemented
+ @return
+ object factory
+ */
+ public static XSingleComponentFactory createComponentFactory(
+ Class impl_class, String supported_services [] )
+ throws com.sun.star.uno.RuntimeException
+ {
+ return createComponentFactory(
+ impl_class, impl_class.getName(), supported_services );
+ }
+ /** Writes component's implementation info to given registry key.
+
+ @param impl_name
+ name of implementation
+ @param supported_services
+ supported services of implementation
+ @param xKey
+ registry key to write to
+ @return
+ success
+ */
+ public static boolean writeRegistryServiceInfo(
+ String impl_name, String supported_services [], XRegistryKey xKey )
+ {
+ try
+ {
+ XRegistryKey xNewKey = xKey.createKey( "/" + impl_name + "/UNO/SERVICES" );
+ for ( int nPos = 0; nPos < supported_services.length; ++nPos )
+ {
+ xNewKey.createKey( supported_services[ nPos ] );
+ }
+ return true;
+ }
+ catch (com.sun.star.registry.InvalidRegistryException exc)
+ {
+ if (DEBUG)
+ {
+ System.err.println(
+ "##### " + Factory.class.getName() + ".writeRegistryServiceInfo -- exc: " +
+ exc.toString() );
+ }
+ }
+ return false;
+ }
+
+
+ private final String m_impl_name;
+ private final String [] m_supported_services;
+ private final Class<?> m_impl_class;
+ private final java.lang.reflect.Method m_method;
+ private final java.lang.reflect.Constructor m_ctor;
+
+ private Factory(
+ Class impl_class, String impl_name, String supported_services [] )
+ throws com.sun.star.uno.DeploymentException
+ {
+ m_impl_name = impl_name;
+ m_supported_services = supported_services;
+ m_impl_class = impl_class;
+
+ Class params [] = new Class [] { XComponentContext.class };
+
+ if (!java.lang.reflect.Modifier.isPublic( impl_class.getModifiers() ))
+ {
+ throw new com.sun.star.uno.DeploymentException("class " + impl_class + " is not public");
+ }
+
+ java.lang.reflect.Method tmpMethod = null;
+ try
+ {
+ // seeking for "public static Object __create( XComponentContext )"
+ tmpMethod = m_impl_class.getMethod( "__create", params );
+ int mod = tmpMethod.getModifiers();
+ if (!tmpMethod.getReturnType().equals( Object.class ) ||
+ !java.lang.reflect.Modifier.isStatic( mod ) ||
+ !java.lang.reflect.Modifier.isPublic( mod ))
+ {
+ tmpMethod = null;
+ }
+ }
+ catch (Exception exc)
+ {
+ }
+ m_method = tmpMethod;
+
+
+ java.lang.reflect.Constructor tmpCtor = null;
+ if (null == m_method)
+ {
+ try
+ {
+ // ctor with context
+ tmpCtor = m_impl_class.getConstructor( params );
+ }
+ catch (Exception exc)
+ {
+ }
+ if (tmpCtor != null)
+ {
+ if (!java.lang.reflect.Modifier.isPublic( tmpCtor.getModifiers() ))
+ {
+ throw new com.sun.star.uno.DeploymentException("constructor with XComponentContext param for class " + impl_class + " is not public");
+ }
+ }
+ else
+ {
+ // else take default ctor
+ java.lang.reflect.Constructor defaultCtor;
+ try
+ {
+ defaultCtor = m_impl_class.getConstructor(new Class[0]);
+ }
+ catch (Exception exc2)
+ {
+ throw new com.sun.star.uno.DeploymentException(exc2, "class " + impl_class + " has no means of creating it, cannot find a __create method or a useful constructor.");
+ }
+ if (!java.lang.reflect.Modifier.isPublic( defaultCtor.getModifiers() ))
+ {
+ throw new com.sun.star.uno.DeploymentException("default constructor for class " + impl_class + " is not public");
+ }
+ }
+ }
+ m_ctor = tmpCtor;
+ }
+
+
+ private Object instantiate( XComponentContext xContext )
+ throws com.sun.star.uno.Exception
+ {
+ try
+ {
+ if (DEBUG)
+ System.out.print( "instantiating " + m_impl_class.toString() + " using " );
+ if (null != m_method)
+ {
+ if (DEBUG)
+ System.out.println( "__create( XComponentContext )..." );
+ return m_method.invoke( null, new Object [] { xContext } );
+ }
+ if (null != m_ctor)
+ {
+ if (DEBUG)
+ System.out.println( "ctor( XComponentContext )..." );
+ return m_ctor.newInstance( new Object [] { xContext } );
+ }
+ if (DEBUG)
+ System.out.println( "default ctor..." );
+ return m_impl_class.newInstance(); // default ctor
+ }
+ catch (java.lang.reflect.InvocationTargetException exc)
+ {
+ Throwable targetException = exc.getTargetException();
+ if (targetException instanceof java.lang.RuntimeException)
+ throw (java.lang.RuntimeException)targetException;
+ else if (targetException instanceof com.sun.star.uno.RuntimeException)
+ throw (com.sun.star.uno.RuntimeException)targetException;
+ else if (targetException instanceof com.sun.star.uno.Exception)
+ throw (com.sun.star.uno.Exception)targetException;
+ else
+ throw new com.sun.star.uno.Exception(targetException, targetException.getMessage(), this);
+ }
+ catch (IllegalAccessException exc)
+ {
+ throw new com.sun.star.uno.RuntimeException( exc, exc.getMessage(), this);
+ }
+ catch (InstantiationException exc)
+ {
+ throw new com.sun.star.uno.RuntimeException( exc, exc.getMessage(), this);
+ }
+ }
+ // XSingleComponentFactory impl
+
+ public final Object createInstanceWithContext(
+ XComponentContext xContext )
+ throws com.sun.star.uno.Exception
+ {
+ return instantiate( xContext );
+ }
+
+ public final Object createInstanceWithArgumentsAndContext(
+ Object arguments [], XComponentContext xContext )
+ throws com.sun.star.uno.Exception
+ {
+ Object inst = instantiate( xContext );
+ XInitialization xInit = UnoRuntime.queryInterface(
+ XInitialization.class, inst );
+ if (null == xInit)
+ {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "cannot pass arguments to component; no XInitialization implemented!", this,
+ (short)0 );
+ }
+ xInit.initialize( arguments );
+ return inst;
+ }
+
+ // XServiceInfo impl
+
+ public final String getImplementationName()
+ {
+ return m_impl_name;
+ }
+
+ public final boolean supportsService( String service_name )
+ {
+ for ( int nPos = 0; nPos < m_supported_services.length; ++nPos )
+ {
+ if (m_supported_services[ nPos ].equals( service_name ))
+ return true;
+ }
+ return false;
+ }
+
+ public final String [] getSupportedServiceNames()
+ {
+ return m_supported_services;
+ }
+}
+
diff --git a/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java b/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java
new file mode 100644
index 000000000..e858ced81
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java
@@ -0,0 +1,864 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Collection;
+
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XEventListener;
+import com.sun.star.uno.UnoRuntime;
+
+/**
+ * This class is a container for interfaces.
+ *
+ * It is intended to be used as storage for UNO interface of a specific type.
+ * The client has to ensure that the container contains only elements of the same
+ * type. If one needs to store different types, then one uses OMultiTypeInterfaceContainer.
+ * When the client calls disposeAndClear, the contained objects are queried for
+ * com.sun.star.lang.XEventListener and disposing is called. Afterwards
+ * the list cannot be used anymore.
+ *
+ * This list does not allow null values.
+ * All methods are thread-safe. The same holds true for
+ * iterators, issued by this class. Several iterators can exist at the same time and can also
+ * be modified (java.util.ListIterator.add, java.util.ListIterator.remove etc.). To make this work,
+ * the InterfaceContainer provides the iterators with copies of the list's data.
+ * The add and remove calls on the iterator modify the data in the iterator's list as well as
+ * in InterfaceContainer. Modification on InterfaceContainer, however, are not
+ * synchronized with existing iterators. For example
+ * <pre>
+ * InterfaceContainer cont= new InterfaceContainer();
+ * ListIterator it= cont.listIterator();
+ *
+ * cont.add( someInterface);
+ * // one cannot obtain someInterface through iterator it,
+ * // instead get a new iterator
+ * it= cont.listIterator();
+ * // it now keeps a fresh copy of cont and hence contains someInterface
+ *
+ * // Adding an interface on the iterator will cause the interface also to be added
+ * // to InterfaceContainer
+ * it.add( someOtherInterface);
+ * // someOtherInterface is now in it and cont
+ * ListIterator it2= cont.listIterator();
+ * //someOtherInterface can also be obtained by all newly created iterators, e.g. it2.
+ * </pre>
+ *
+ * The add and remove methods of an iterator work on a particular location within a list,
+ * dependent on what the value of the iterator's cursor is. After the call the value at the
+ * appropriate position has been modified. Since the iterator received a copy of InterfaceContainer's
+ * data, InterfaceContainer may have been modified (by List methods or through other iterators).
+ * Therefore both data sets may not contain the same elements anymore. Consequently, a List method
+ * that modifies data, does not modify InterfaceContainer's data at a certain index
+ * (according to the iterators cursor). Instead, new elements are added at the end of list. When
+ * Iterator.remove is called, then the first occurrence of that element in InterfaceContainer
+ * is removed.
+ * ListIterator.set is not supported.
+ *
+ * A lot of methods resemble those of the to java.util.List interface, although
+ * this class does not implement it. However, the list iterators returned, for example by
+ * the listIterator method implement the java.util.ListIterator interface.
+ * Implementing the List interface would mean to support all none - optional methods as
+ * prescribed by the interface declaration. Among those is the subList method which returns
+ * a range of values of the list's data wrapped in a List implementation. Changes to the sub
+ * list have to cause changes in the main list. This is a problem, since this class is to be
+ * used in a multi-threaded environment. The sub list could work on a copy as the iterators
+ * do, but all the functions which work on a given index could not be properly supported.
+ * Unfortunately, the List interface documentation states that all optional methods implemented
+ * by the list have to be implemented in the sub list. That would mean to do without all those
+ * critical methods, although they might work well in the "main list" (as opposed to sub list).
+ */
+public class InterfaceContainer implements Cloneable
+{
+ static final boolean DEBUG= false;
+ /**
+ * The array buffer into which the elements of the ArrayList are stored.
+ * The capacity of the ArrayList is the length of this array buffer.
+ */
+ Object elementData[];
+
+ /**
+ * The size of the ArrayList (the number of elements it contains).
+ */
+ private int size;
+
+
+ /** Creates a new instance of InterfaceContainer */
+ public InterfaceContainer()
+ {
+ this(10);
+ }
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ *
+ * @param initialCapacity the initial capacity of the list.
+ * @exception IllegalArgumentException if the specified initial capacity
+ * is negative
+ */
+ public InterfaceContainer(int initialCapacity)
+ {
+ if (initialCapacity < 0)
+ throw new java.lang.IllegalArgumentException("Illegal Capacity: "+
+ initialCapacity);
+ this.elementData = new Object[initialCapacity];
+ }
+
+ private InterfaceContainer(Object[] data) {
+ elementData = data;
+ size = elementData == null ? 0 : elementData.length;
+ }
+
+ /**
+ * Trims the capacity of this <code>ArrayList</code> instance to be the
+ * list's current size. An application can use this operation to minimize
+ * the storage of an <code>ArrayList</code> instance.
+ */
+ synchronized public void trimToSize()
+ {
+ int oldCapacity = elementData.length;
+ if (size < oldCapacity)
+ {
+ Object oldData[] = elementData;
+ elementData = new Object[size];
+ System.arraycopy(oldData, 0, elementData, 0, size);
+ }
+ }
+
+ /**
+ * Increases the capacity of this <code>ArrayList</code> instance, if
+ * necessary, to ensure that it can hold at least the number of elements
+ * specified by the minimum capacity argument.
+ *
+ * @param minCapacity the desired minimum capacity.
+ */
+ synchronized public void ensureCapacity(int minCapacity)
+ {
+ int oldCapacity = elementData.length;
+ if (minCapacity > oldCapacity)
+ {
+ Object oldData[] = elementData;
+ int newCapacity = (oldCapacity * 3)/2 + 1;
+ if (newCapacity < minCapacity)
+ newCapacity = minCapacity;
+ elementData = new Object[newCapacity];
+ System.arraycopy(oldData, 0, elementData, 0, size);
+ }
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ *
+ * @param o element to be appended to this list.
+ * @return <code>true</code> (as per the general contract of Collection.add).
+ */
+ synchronized public boolean add(Object o)
+ {
+ boolean ret= false;
+ if (elementData != null && o != null)
+ {
+ ensureCapacity(size + 1); // Increments modCount!!
+ elementData[size++] = o;
+ ret= true;
+ }
+ return ret;
+ }
+
+ /**
+ * Inserts the specified element at the specified position in this
+ * list. Shifts the element currently at that position (if any) and
+ * any subsequent elements to the right (adds one to their indices).
+ *
+ * @param index index at which the specified element is to be inserted.
+ * @param element element to be inserted.
+ * @throws IndexOutOfBoundsException if index is out of range
+ * <code>(index &lt; 0 || index &gt; size())</code>.
+ */
+ synchronized public void add(int index, Object element)
+ {
+ if (elementData != null && element != null)
+ {
+ if (index > size || index < 0)
+ throw new IndexOutOfBoundsException(
+ "Index: "+index+", Size: "+size);
+
+ ensureCapacity(size+1);
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ elementData[index] = element;
+ size++;
+ }
+ }
+
+
+ /**
+ * Appends all of the elements in the specified Collection to the end of
+ * this list, in the order that they are returned by the
+ * specified Collection's Iterator. The behavior of this operation is
+ * undefined if the specified Collection is modified while the operation
+ * is in progress. (This implies that the behavior of this call is
+ * undefined if the specified Collection is this list, and this
+ * list is nonempty.)
+ *
+ * @param c the elements to be inserted into this list.
+ * @throws IndexOutOfBoundsException if index out of range <code>(index
+ * &lt; 0 || index &gt; size())</code>.
+ * @return true if an element was inserted.
+ */
+ synchronized public boolean addAll(Collection c)
+ {
+ int numNew = c.size();
+ ensureCapacity(size + numNew);
+
+ Iterator e = c.iterator();
+ for (int i=0; i<numNew; i++)
+ {
+ Object o= e.next();
+ if (o != null)
+ elementData[size++] = o;
+ }
+ return numNew != 0;
+ }
+ /**
+ * Inserts all of the elements in the specified Collection into this
+ * list, starting at the specified position. Shifts the element
+ * currently at that position (if any) and any subsequent elements to
+ * the right (increases their indices). The new elements will appear
+ * in the list in the order that they are returned by the
+ * specified Collection's iterator.
+ *
+ * @param index index at which to insert first element
+ * from the specified collection.
+ * @param c elements to be inserted into this list.
+ * @throws IndexOutOfBoundsException if index out of range <code>(index
+ * &lt; 0 || index &gt; size())</code>.
+ * @return true if an element was inserted.
+ */
+ synchronized public boolean addAll(int index, Collection c)
+ {
+ boolean ret= false;
+ if (elementData != null)
+ {
+ if (index > size || index < 0)
+ throw new IndexOutOfBoundsException(
+ "Index: "+index+", Size: "+size);
+ // only add the non-null elements
+ int sizeCol= c.size();
+ Object[] arColl= new Object[sizeCol];
+ Iterator icol= c.iterator();
+ int curIndex= 0;
+ for (int i=0; i < sizeCol; i++)
+ {
+ Object o= icol.next();
+ if (o != null)
+ arColl[curIndex++]= o;
+ }
+ int numNew = curIndex;
+ ensureCapacity(size + numNew); // Increments modCount!!
+
+ int numMoved = size - index;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index, elementData, index + numNew,
+ numMoved);
+
+ for (int i=0; i<numNew; i++)
+ {
+ elementData[index++]= arColl[i];
+ }
+ size += numNew;
+ ret= numNew != 0;
+ }
+ return ret;
+ }
+
+ /**
+ * Removes all of the elements from this list. The list will
+ * be empty after this call returns.
+ */
+ synchronized public void clear()
+ {
+ if (elementData != null)
+ {
+ // Let gc do its work
+ for (int i = 0; i < size; i++)
+ elementData[i] = null;
+
+ size = 0;
+ }
+ }
+ /**
+ * Returns <code>true</code> if this list contains the specified element.
+ *
+ * @param elem element whose presence in this List is to be tested.
+ * @return <code>true</code> if this list contains the specified element.
+ */
+ synchronized public boolean contains(Object elem)
+ {
+ return indexOf(elem) >= 0;
+ }
+
+ synchronized public boolean containsAll(Collection collection)
+ {
+ boolean retVal= true;
+ if (elementData != null && collection != null)
+ {
+ Iterator it= collection.iterator();
+ while (it.hasNext())
+ {
+ Object obj= it.next();
+ if (!contains(obj))
+ {
+ retVal= false;
+ break;
+ }
+ }
+ }
+ return retVal;
+ }
+ /**
+ * Returns the element at the specified position in this list.
+ *
+ * @param index index of element to return.
+ * @return the element at the specified position in this list.
+ * @throws IndexOutOfBoundsException if index is out of range <code>(index
+ * &lt; 0 || index &gt;= size())</code>.
+ */
+ synchronized public Object get(int index)
+ {
+ if (elementData != null)
+ {
+ RangeCheck(index);
+ return elementData[index];
+ }
+ return null;
+ }
+
+ /**
+ * Searches for the first occurrence of the given argument, testing
+ * for equality using the <code>equals</code> method.
+ *
+ * @param elem an object.
+ * @return the index of the first occurrence of the argument in this
+ * list; returns <code>-1</code> if the object is not found.
+ * @see Object#equals(Object)
+ */
+ synchronized public int indexOf(Object elem)
+ {
+ if (elementData == null || elem == null) {
+ return -1;
+ }
+
+ int index= -1;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (elem == elementData[i])
+ {
+ index= i;
+ break;
+ }
+ }
+
+ if (index == -1)
+ {
+ for (int i = 0; i < size; i++)
+ {
+ if (UnoRuntime.areSame(elem, elementData[i]))
+ {
+ index= i;
+ break;
+ }
+ }
+ }
+ return index;
+ }
+ /**
+ * Tests if this list has no elements.
+ *
+ * @return <code>true</code> if this list has no elements;
+ * <code>false</code> otherwise.
+ */
+ synchronized public boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ synchronized public Iterator iterator()
+ {
+ if (elementData != null)
+ {
+ InterfaceContainer aCopy= (InterfaceContainer) clone();
+ return new Itr(aCopy);
+ }
+ return null;
+ }
+ /**
+ * Returns the index of the last occurrence of the specified object in
+ * this list.
+ *
+ * @param elem the desired element.
+ * @return the index of the last occurrence of the specified object in
+ * this list; returns -1 if the object is not found.
+ */
+ synchronized public int lastIndexOf(Object elem)
+ {
+ if (elementData == null || elem == null) {
+ return -1;
+ }
+
+ int index= -1;
+
+ for (int i = size-1; i >= 0; i--)
+ {
+ if (elem == elementData[i])
+ {
+ index= i;
+ break;
+ }
+ }
+ if (index == -1)
+ {
+ for (int i = size-1; i >= 0; i--)
+ {
+ if (UnoRuntime.areSame(elem, elementData[i]))
+ {
+ index= i;
+ break;
+ }
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Returns a shallow copy of this <code>ArrayList</code> instance. The contained
+ * references are copied but the objects not.
+ *
+ * @return a clone of this <code>List</code> instance.
+ */
+ @Override
+ synchronized public Object clone()
+ {
+ Object[] data;
+ if (elementData == null) {
+ data = null;
+ } else {
+ data = new Object[size];
+ System.arraycopy(elementData, 0, data, 0, size);
+ }
+ return new InterfaceContainer(data);
+ }
+ synchronized public ListIterator listIterator()
+ {
+ return listIterator(0);
+ }
+
+ /** The iterator keeps a copy of the list. Changes to InterfaceContainer do not
+ * affect the data of the iterator. Conversely, changes to the iterator are effect
+ * InterfaceContainer.
+ * @param index the starting offset into the list.
+ * @return a new iterator.
+ */
+ synchronized public ListIterator listIterator(int index)
+ {
+ if (elementData != null)
+ {
+ InterfaceContainer aCopy= (InterfaceContainer) clone();
+ return new LstItr(aCopy, index);
+ }
+ return null;
+ }
+ /**
+ * Removes the element at the specified position in this list.
+ * Shifts any subsequent elements to the left (subtracts one from their
+ * indices).
+ *
+ * @param index the index of the element to removed.
+ * @return the element that was removed from the list.
+ * @throws IndexOutOfBoundsException if index out of range <code>(index
+ * &lt; 0 || index &gt;= size())</code>.
+ */
+ synchronized public Object remove(int index)
+ {
+ Object ret= null;
+ if (elementData != null)
+ {
+ RangeCheck(index);
+ ret= elementData[index];
+
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // Let gc do its work
+ }
+ return ret;
+ }
+
+
+ /** Parameter obj may... or may not. What did the original author want
+ * to tell us here?
+ * @param obj the object to be removed.
+ * @return true if obj was successfully removed from the list.
+ */
+ synchronized public boolean remove(Object obj)
+ {
+ boolean ret= false;
+ if (elementData != null && obj != null)
+ {
+ int index= indexOf(obj);
+ if (index != -1)
+ {
+ ret= true;
+ remove(index);
+ }
+ }
+ return ret;
+ }
+
+ synchronized public boolean removeAll(Collection collection)
+ {
+ boolean retVal= false;
+ if (elementData != null && collection != null)
+ {
+ Iterator it= collection.iterator();
+ while (it.hasNext())
+ {
+ Object obj= it.next();
+ boolean bMod= remove( obj);
+ if (bMod)
+ retVal= true;
+ }
+ }
+ return retVal;
+ }
+
+ synchronized public boolean retainAll(Collection collection)
+ {
+ if (elementData == null || collection == null) {
+ return false;
+ }
+
+ boolean retVal= false;
+ // iterate over data
+ Object[] arRetained= new Object[size];
+ int indexRetained= 0;
+ for(int i= 0; i < size; i++)
+ {
+ Object curElem= elementData[i];
+ // try to find the element in collection
+ Iterator itColl= collection.iterator();
+ boolean bExists= false;
+ while (itColl.hasNext())
+ {
+ if (curElem == itColl.next())
+ {
+ // current element is in collection
+ bExists= true;
+ break;
+ }
+ }
+ if (!bExists)
+ {
+ itColl= collection.iterator();
+ while (itColl.hasNext())
+ {
+ Object o= itColl.next();
+ if (o != null && UnoRuntime.areSame(o, curElem))
+ {
+ bExists= true;
+ break;
+ }
+ }
+ }
+ if (bExists)
+ arRetained[indexRetained++]= curElem;
+ }
+ retVal= size != indexRetained;
+ if (indexRetained > 0)
+ {
+ elementData= arRetained;
+ size= indexRetained;
+ }
+ return retVal;
+ }
+
+
+ /** Not supported.
+ * @param index index of element to replace.
+ * @param element element to be stored at the specified position.
+ * @return the element previously at the specified position.
+ * @throws IndexOutOfBoundsException if index out of range
+ * <code>(index &lt; 0 || index &gt;= size())</code>.
+ */
+ synchronized public Object set(int index, Object element)
+ {
+ Object ret= null;
+ if (elementData != null && element != null)
+ {
+ RangeCheck(index);
+ ret = elementData[index];
+ elementData[index] = element;
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ synchronized public int size()
+ {
+ if (elementData != null)
+ return size;
+ return 0;
+ }
+
+
+ /**
+ * Returns an array containing all of the elements in this list
+ * in the correct order.
+ *
+ * @return an array containing all of the elements in this list
+ * in the correct order.
+ */
+ synchronized public Object[] toArray()
+ {
+ if (elementData != null)
+ {
+ Object[] result = new Object[size];
+ System.arraycopy(elementData, 0, result, 0, size);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array containing all of the elements in this list in the
+ * correct order. The runtime type of the returned array is that of the
+ * specified array. If the list fits in the specified array, it is
+ * returned therein. Otherwise, a new array is allocated with the runtime
+ * type of the specified array and the size of this list.<p>
+ *
+ * If the list fits in the specified array with room to spare (i.e., the
+ * array has more elements than the list), the element in the array
+ * immediately following the end of the collection is set to
+ * <code>null</code>. This is useful in determining the length of the list
+ * <i>only</i> if the caller knows that the list does not contain any
+ * <code>null</code> elements.
+ *
+ * @param a the array into which the elements of the list are to
+ * be stored, if it is big enough; otherwise, a new array of the
+ * same runtime type is allocated for this purpose.
+ * @return an array containing the elements of the list.
+ * @throws ArrayStoreException if the runtime type of a is not a supertype
+ * of the runtime type of every element in this list.
+ */
+ synchronized public Object[] toArray(Object a[])
+ {
+ if (a.length < size)
+ a = (Object[])java.lang.reflect.Array.newInstance(
+ a.getClass().getComponentType(), size);
+ if (elementData != null)
+ System.arraycopy(elementData, 0, a, 0, size);
+
+ if (a.length > size)
+ a[size] = null;
+
+ return a;
+ }
+
+ /**
+ * Check if the given index is in range. If not, throw an appropriate
+ * runtime exception.
+ */
+ private void RangeCheck(int index)
+ {
+ if (index >= size || index < 0)
+ throw new IndexOutOfBoundsException(
+ "Index: "+index+", Size: "+size);
+ }
+
+ public void disposeAndClear(EventObject evt)
+ {
+ Iterator aIt;
+ synchronized (this)
+ {
+ aIt= iterator();
+ // Release containers if new entries occur in disposing;
+ // set the member to null, the iterator delete the values
+ clear();
+ elementData= null;
+ size= 0;
+ }
+ if (aIt != null)
+ {
+ while( aIt.hasNext() )
+ {
+ try
+ {
+ Object o= aIt.next();
+ XEventListener evtListener= UnoRuntime.queryInterface(
+ XEventListener.class, o);
+ if( evtListener != null )
+ evtListener.disposing( evt );
+ }
+ catch ( RuntimeException e)
+ {
+ // be robust, if e.g. a remote bridge has disposed already.
+ // there is no way, to delegate the error to the caller :o(.
+ }
+ }
+ }
+ }
+
+
+ private class Itr implements Iterator
+ {
+ InterfaceContainer dataIt;
+ /**
+ * Index of element to be returned by subsequent call to next.
+ */
+ int cursor= 0;
+ /**
+ * Index of element returned by most recent call to next or
+ * previous. Reset to -1 if this element is deleted by a call
+ * to remove.
+ */
+ int lastRet = -1;
+
+ /** The object that has been returned by most recent call to next
+ * or previous. Reset to null if this element is deleted by a call
+ * to remove.
+ */
+ Object lastRetObj= null;
+
+ Itr(InterfaceContainer _data)
+ {
+ dataIt= _data;
+ }
+
+ synchronized public boolean hasNext()
+ {
+ return cursor !=dataIt.size();
+ }
+
+ public synchronized Object next()
+ {
+ try
+ {
+ Object next = dataIt.get(cursor);
+ lastRet = cursor++;
+ lastRetObj= next;
+ return next;
+ }
+ catch(java.lang.IndexOutOfBoundsException e)
+ {
+ java.util.NoSuchElementException ex2 = new java.util.NoSuchElementException();
+ ex2.initCause(e);
+ throw ex2;
+ }
+ }
+
+ /** Removes the interface from the list, that has been last returned by a
+ * call to next(). This is done according to the specification of the interface
+ * method. The element is also removed from InterfaceContainer but independent
+ * of the location. If the element is multiple times in InterfaceContainer then
+ * it is up to the java.util.ArrayList implementation what element is removed.
+ */
+ public synchronized void remove()
+ {
+ if (lastRet == -1)
+ throw new IllegalStateException();
+ // Remove the entry from InterfaceContainer.
+ InterfaceContainer.this.remove(lastRetObj);
+ dataIt.remove(lastRet);
+
+ if (lastRet < cursor)
+ cursor--;
+ lastRet = -1;
+ lastRetObj= null;
+ }
+ }
+
+ private class LstItr extends Itr implements ListIterator
+ {
+
+ LstItr(InterfaceContainer _data, int _index)
+ {
+ super(_data);
+ cursor= _index;
+ }
+
+ /** Inserts an element to the iterators list according to the specification
+ * of this interface method. The element is also added to InterfaceContainer
+ * but its location within the list cannot be guaranteed.
+ */
+ public synchronized void add(Object o)
+ {
+ InterfaceContainer.this.add(o);
+ dataIt.add(cursor++, o);
+ lastRet = -1;
+ lastRetObj= null;
+ }
+
+ synchronized public boolean hasPrevious()
+ {
+ return cursor != 0;
+ }
+
+ synchronized public int nextIndex()
+ {
+ return cursor;
+ }
+
+ public synchronized Object previous()
+ {
+ try
+ {
+ Object previous = dataIt.get(--cursor);
+ lastRet = cursor;
+ lastRetObj= previous;
+ return previous;
+ } catch(IndexOutOfBoundsException e)
+ {
+ java.util.NoSuchElementException ex2 = new java.util.NoSuchElementException();
+ ex2.initCause(e);
+ throw ex2;
+ }
+ }
+
+ synchronized public int previousIndex()
+ {
+ return cursor-1;
+ }
+
+ /** This is not possible since several iterators can modify InterfaceContainer
+ */
+ public synchronized void set(Object o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+
+ } // class LstItr
+}
+
diff --git a/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java b/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java
new file mode 100644
index 000000000..9b061f81c
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+import com.sun.star.uno.Type;
+import com.sun.star.lang.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+public class MultiTypeInterfaceContainer
+{
+
+ private final Map<Object,InterfaceContainer> map= new HashMap<Object,InterfaceContainer>();
+
+ /** only returns types which have at least one value in InterfaceContainer
+ * return value can contain an element null, if someone called
+ * addInterface (null, interf)
+ * @return an array of types in this container.
+ */
+ synchronized public Type[] getContainedTypes()
+ {
+ int size;
+ Type[] retVal= null;
+
+ if ( (size=map.size()) > 0)
+ {
+ Type [] arTypes= new Type[size];
+
+ int countTypes= 0;
+ for (Map.Entry<Object,InterfaceContainer> entry : map.entrySet())
+ {
+ InterfaceContainer cont= entry.getValue();
+ if (cont != null && cont.size() > 0)
+ {
+ Object key = entry.getKey();
+ if (key == null)
+ arTypes[countTypes++]= new Type();
+ else if (key instanceof Type)
+ arTypes[countTypes++]= (Type) key;
+ else if (key instanceof Class)
+ arTypes[countTypes++]= new Type((Class) key);
+ else
+ arTypes[countTypes++]= new Type(key.getClass());
+ }
+ }
+
+ if (countTypes != size)
+ {
+ retVal= new Type[countTypes];
+ System.arraycopy(arTypes, 0, retVal, 0, countTypes);
+ }
+ else
+ retVal= arTypes;
+ }
+ if (retVal == null)
+ retVal= new Type[0];
+ return retVal;
+ }
+
+ /** param key can be null
+ * @param key the object for which the container should be retrieved.
+ * @return the container that contains the object key, if any.
+ */
+ synchronized public InterfaceContainer getContainer(Object key)
+ {
+ InterfaceContainer retVal= null;
+ for (Map.Entry<Object,InterfaceContainer> entry : map.entrySet())
+ {
+ Object obj= entry.getKey();
+ if (obj == null && key == null)
+ {
+ retVal= map.get(null);
+ break;
+ }
+ else if( obj != null && obj.equals(key))
+ {
+ retVal= entry.getValue();
+ break;
+ }
+ }
+ return retVal;
+ }
+
+
+ synchronized public int addInterface(Object ckey, Object iface)
+ {
+ //If the key is a Type then it does not matter if the objects are different
+ // if they represent the same type. This is because Types overrides hashCode and
+ // equals. For example:
+ // Type a= new Type(XInterface.class);
+ // Type b= new Type(XInterface.class);
+ // Although a != b , the map interprets both as being the same.
+ InterfaceContainer cont= map.get(ckey);
+ if (cont != null)
+ {
+ cont.add(iface);
+ }
+ else
+ {
+ cont= new InterfaceContainer();
+ cont.add(iface);
+ map.put(ckey, cont);
+ }
+ return cont.size();
+ }
+
+
+ synchronized public int removeInterface(Object key, Object iface)
+ {
+ int retVal= 0;
+ InterfaceContainer cont= map.get(key);
+ if (cont != null)
+ {
+ cont.remove(iface);
+ retVal= cont.size();
+ }
+ return retVal;
+ }
+
+ public void disposeAndClear(EventObject evt)
+ {
+ Iterator<InterfaceContainer> it= null;
+ synchronized(this)
+ {
+ it= map.values().iterator();
+ }
+ while (it.hasNext() ) {
+ it.next().disposeAndClear(evt);
+ }
+ }
+
+ synchronized public void clear()
+ {
+ Iterator<InterfaceContainer> it= map.values().iterator();
+ while (it.hasNext()) {
+ it.next().clear();
+ }
+ }
+}
diff --git a/ridljar/com/sun/star/lib/uno/helper/PropertySet.java b/ridljar/com/sun/star/lib/uno/helper/PropertySet.java
new file mode 100644
index 000000000..67a4f0c98
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/PropertySet.java
@@ -0,0 +1,1103 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.helper;
+
+import com.sun.star.uno.Type;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.AnyConverter;
+import com.sun.star.uno.XInterface;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.beans.XPropertyChangeListener;
+import com.sun.star.beans.XVetoableChangeListener;
+import com.sun.star.beans.PropertyChangeEvent;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.Property;
+import com.sun.star.beans.PropertyAttribute;
+import com.sun.star.beans.UnknownPropertyException;
+import com.sun.star.beans.XPropertiesChangeListener;
+import com.sun.star.beans.XPropertySetInfo;
+import com.sun.star.beans.XFastPropertySet;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.beans.XMultiPropertySet;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.HashMap;
+import java.lang.reflect.Field;
+import com.sun.star.lang.DisposedException;
+
+
+/** This class is an implementation of the interfaces com.sun.star.beans.XPropertySet,
+ * com.sun.star.beans.XFastPropertySet and com.sun.star.beans.XMultiPropertySet. This
+ * class has to be inherited to be used. The values of properties are stored in member
+ * variables of the inheriting class. By overriding the methods
+ * {@link #convertPropertyValue convertPropertyValue},
+ * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and
+ * {@link #getPropertyValue(Property)} one can determine how
+ * property values are stored.
+ * When using the supplied implementations of this class then the member variables which
+ * hold property values have to be declared in the class which inherits last in the inheriting
+ * chain and they have to be public<p>
+ * Properties have to be registered by one of the registerProperty methods. They take among other
+ * arguments an Object named <em>id</em> which has to be a String that represents the name of
+ * the member variable. The registering has to occur in the constructor of the inheriting class.
+ * It is no allowed to add or change properties later on.<p>
+ * Example:
+ * <pre>
+ * public class Foo extends PropertySet
+ * {
+ * protected int intProp;
+ *
+ * public Foo()
+ * {
+ * registerProperty("PropertyA", 0, new Type(int.class), (short)0, "intProp");
+ * }
+ * }
+ *
+ * </pre>
+ */
+public class PropertySet extends ComponentBase implements XPropertySet, XFastPropertySet,
+XMultiPropertySet
+{
+ private HashMap<String,Property> _nameToPropertyMap;
+ private HashMap<Integer,Property> _handleToPropertyMap;
+ private HashMap<Property,Object> _propertyToIdMap;
+ private Property[] arProperties;
+
+ private int lastHandle= 1;
+
+ protected XPropertySetInfo propertySetInfo;
+ protected MultiTypeInterfaceContainer aBoundLC= new MultiTypeInterfaceContainer();
+ protected MultiTypeInterfaceContainer aVetoableLC= new MultiTypeInterfaceContainer();
+ public PropertySet()
+ {
+ super();
+ initMappings();
+ }
+
+ /** Registers a property with this helper class and associates the argument <em>id</em> with it.
+ * <em>id</em> is used to identify the storage of the property value. How property values are stored
+ * and retrieved is determined by the methods {@link #convertPropertyValue convertPropertyValue},
+ * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and {@link #getPropertyValue(Property) getPropertyValue}
+ * These methods expect <em>id</em> to be a String which represents the name of a member variable
+ * which holds the property value.
+ * Only properties which are registered can be accessed. Registration has to occur during
+ * initialization of the inheriting class (i.e. within the constructor).
+ * @param prop The property to be registered.
+ * @param id Identifies the properties storage.
+ * @see #getPropertyId
+ */
+ protected void registerProperty(Property prop, Object id)
+ {
+ putProperty(prop);
+ assignPropertyId(prop, id);
+ }
+
+ /** Registers a property with this helper class and associates the argument id with it.
+ * It does the same as {@link #registerProperty(Property, Object)}. The first four
+ * arguments are used to construct a Property object.
+ * Registration has to occur during
+ * initialization of the inheriting class (i.e. within the constructor)
+ * @param name The property's name (Property.Name).
+ * @param handle The property's handle (Property.Handle).
+ * @param type The property's type (Property.Type).
+ * @param attributes The property's attributes (Property.Attributes).
+ * @param id Identifies the property's storage.
+ */
+ protected void registerProperty(String name, int handle, Type type, short attributes, Object id)
+ {
+ Property p= new Property(name, handle, type, attributes);
+ registerProperty(p, id);
+ }
+
+ /** Registers a property with this class and associates the argument id with it.
+ * It does the same as {@link #registerProperty(Property, Object)}. The first three
+ * arguments are used to construct a Property object. The value for the Property.Handle
+ * is generated and does not have to be specified here. Use this method for registering
+ * a property if you do not care about the Property's handles.
+ * Registration has to occur during
+ * initialization of the inheriting class (i.e. within the constructor).
+ * @param name The property's name (Property.Name).
+ * @param type The property's type (Property.Type).
+ * @param attributes The property's attributes (Property.Attributes).
+ * @param id Identifies the property's storage.
+ */
+ protected void registerProperty(String name, Type type, short attributes, Object id)
+ {
+ Property p= new Property(name, lastHandle++, type, attributes);
+ registerProperty(p, id);
+ }
+
+ /** Registers a property with this class. This method expects that property values
+ * are stored in member variables as is the case if the methods convertPropertyValue,
+ * setPropertyValueNoBroadcast and getPropertyValue(Property) are not overridden.
+ * It is presumed that the type of the member variable
+ * corresponds Property.Type. For example, if the TypeClass of Property.Type is to be
+ * a TypeClass.SHORT then the member must be a short or java.lang.Short.
+ * The handle for the property is generated.<br>
+ * If there is no member with the specified name or if the member has an incompatible type
+ * then a com.sun.star.uno.RuntimeException is thrown.
+ * @param propertyName The name of the property.
+ * @param memberName The name of the member variable that holds the value of the property.
+ * @param attributes The property attributes.
+ */
+ protected void registerProperty(String propertyName, String memberName, short attributes)
+ {
+ Field propField= null;
+ try
+ {
+ propField= getClass().getDeclaredField(memberName);
+ }
+ catch (NoSuchFieldException e)
+ {
+ throw new com.sun.star.uno.RuntimeException(e, "there is no member variable: " + memberName);
+ }
+ Class cl= propField.getType();
+ Type t= new Type(cl);
+ if (t.getTypeClass() != TypeClass.UNKNOWN)
+ {
+ Property p= new Property(propertyName, lastHandle++, t, attributes);
+ registerProperty(p,memberName);
+ }
+ else
+ throw new com.sun.star.uno.RuntimeException("the member has an unknown type: " + memberName);
+ }
+
+ /** Registers a property with this class.
+ * It is presumed that the name of property is equal to the name of the member variable
+ * that holds the property value.
+ * @param propertyName The name of the property and the member variable that holds the property's value.
+ * @param attributes The property attributes.
+ * @see #registerProperty(String, String, short)
+ */
+ protected void registerProperty(String propertyName, short attributes)
+ {
+ registerProperty(propertyName, propertyName, attributes);
+ }
+
+
+
+ /** Returns the Property object for a given property name or null if that property does
+ * not exists (i.e. it has not been registered). Override this method
+ * if you want to implement your own mapping from property names to Property objects.
+ * Then you also have to override {@link #initMappings}, {@link #getProperties()} and
+ * {@link #putProperty(Property)}.
+ * @param propertyName The name of the property (Property.Name)
+ * @return The Property object with the name <em>propertyName</em>.
+ */
+ protected Property getProperty(String propertyName)
+ {
+ return _nameToPropertyMap.get(propertyName);
+ }
+
+ /** Returns the Property object with a handle (Property.Handle) as specified by the argument
+ * <em>nHandle</em>. The method returns null if there is no such property (i.e. it has not
+ * been registered). Override this method if you want to implement your own mapping from handles
+ * to Property objects. Then you also have to override {@link #initMappings}, {@link #putProperty(Property)}.
+ * @param nHandle The handle of the property (Property.Handle).
+ * @return The Property object with the handle <em>nHandle</em>
+ */
+ protected Property getPropertyByHandle(int nHandle)
+ {
+ return _handleToPropertyMap.get(Integer.valueOf(nHandle));
+ }
+
+ /** Returns an array of all Property objects or an array of length null if there
+ * are no properties. Override this method if you want to implement your own mapping from names
+ * to Property objects. Then you also have to override {@link #initMappings}, {@link #getProperty(String)} and
+ * {@link #putProperty}.
+ * @return Array of all Property objects.
+ */
+ protected Property[] getProperties()
+ {
+ if (arProperties == null)
+ {
+ Collection<Property> values= _nameToPropertyMap.values();
+ arProperties= values.toArray(new Property[_nameToPropertyMap.size()]);
+ }
+ return arProperties;
+ }
+
+ /** Stores a Property object so that it can be retrieved subsequently by
+ * {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}.
+ * Override this method if you want to implement your own mapping from handles
+ * to Property objects and names to Property objects. Then you also need to override {@link #initMappings},
+ * {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}.
+ * @param prop The Property object that is to be stored.
+ */
+ protected void putProperty(Property prop)
+ {
+ _nameToPropertyMap.put(prop.Name, prop);
+ if (prop.Handle != -1)
+ _handleToPropertyMap.put(Integer.valueOf(prop.Handle), prop);
+ }
+
+ /** Assigns an identifier object to a Property object so that the identifier
+ * can be obtained by {@link #getPropertyId getPropertyId} later on. The identifier
+ * is used to specify a certain storage for the property's value. If you do not
+ * override {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} or {@link #getPropertyValue(Property)}
+ * then the argument <em>id</em> has to be a String that equals the name of
+ * the member variable that holds the Property's value.
+ * Override this method if you want to implement your own mapping from Property objects to ids or
+ * if you need ids of a type other than String.
+ * Then you also need to override {@link #initMappings initMappings} and {@link #getPropertyId getPropertyId}.
+ * @param prop The Property object that is being assigned an id.
+ * @param id The object which identifies the storage used for the property's value.
+ * @see #registerProperty(Property, Object)
+ */
+ protected void assignPropertyId(Property prop, Object id)
+ {
+ if (id instanceof String && ((String) id).length() != 0)
+ _propertyToIdMap.put(prop, id);
+ }
+
+ /** Returns the identifier object for a certain Property. The object must have been
+ * previously assigned to the Property object by {@link #assignPropertyId assignPropertyId}.
+ * Override this method if you want to implement your own mapping from Property objects to ids.
+ * Then you also need to override {@link #initMappings initMappings} and {@link #assignPropertyId assignPropertyId}.
+ * @param prop The property for which the id is to be retrieved.
+ * @return The id object that identifies the storage used for the property's value.
+ * @see #registerProperty(Property, Object)
+ */
+ protected Object getPropertyId(Property prop)
+ {
+ return _propertyToIdMap.get(prop);
+ }
+
+ /** Initializes data structures used for mappings of property names to property object,
+ * property handles to property objects and property objects to id objects.
+ * Override this method if you want to implement your own mappings. Then you also need to
+ * override {@link #putProperty putProperty},{@link #getProperty getProperty}, {@link #getPropertyByHandle},
+ * {@link #assignPropertyId assignPropertyId} and {@link #getPropertyId getPropertyId}.
+ */
+ protected void initMappings()
+ {
+ _nameToPropertyMap= new HashMap<String,Property>();
+ _handleToPropertyMap= new HashMap<Integer,Property>();
+ _propertyToIdMap= new HashMap<Property,Object>();
+ }
+
+ /** Makes sure that listeners which are kept in aBoundLC (XPropertyChangeListener) and aVetoableLC
+ * (XVetoableChangeListener) receive a disposing call. Also those listeners are released.
+ */
+ @Override
+ protected void postDisposing()
+ {
+ // Create an event with this as sender
+ EventObject aEvt= new EventObject(this);
+
+ // inform all listeners to release this object
+ aBoundLC.disposeAndClear(aEvt);
+ aVetoableLC.disposeAndClear(aEvt);
+ }
+
+ //XPropertySet ----------------------------------------------------
+ synchronized public void addPropertyChangeListener(String str, XPropertyChangeListener xPropertyChangeListener)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ // only add listeners if you are not disposed
+ if (! bInDispose && ! bDisposed)
+ {
+ if (str.length() > 0)
+ {
+ Property prop= getProperty(str);
+ if (prop == null)
+ throw new UnknownPropertyException("Property " + str + " is unknown");
+
+ // Add listener for a certain property
+ if ((prop.Attributes & PropertyAttribute.BOUND) > 0)
+ aBoundLC.addInterface(str, xPropertyChangeListener);
+ else
+ //ignore silently
+ return;
+ }
+ else
+ // Add listener for all properties
+ listenerContainer.addInterface(XPropertyChangeListener.class, xPropertyChangeListener);
+ }
+ }
+ //XPropertySet ----------------------------------------------------
+ synchronized public void addVetoableChangeListener(String str, com.sun.star.beans.XVetoableChangeListener xVetoableChangeListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
+ {
+ // only add listeners if you are not disposed
+ if (! bInDispose && ! bDisposed)
+ {
+ if (str.length() > 0)
+ {
+ Property prop= getProperty(str);
+ if (prop == null)
+ throw new UnknownPropertyException("Property " + str + " is unknown");
+
+ // Add listener for a certain property
+ if ((prop.Attributes & PropertyAttribute.CONSTRAINED) > 0)
+ aVetoableLC.addInterface(str, xVetoableChangeListener);
+ else
+ //ignore silently
+ return;
+ }
+ else
+ // Add listener for all properties
+ listenerContainer.addInterface(XVetoableChangeListener.class, xVetoableChangeListener);
+ }
+ }
+ //XPropertySet ----------------------------------------------------
+ public synchronized com.sun.star.beans.XPropertySetInfo getPropertySetInfo()
+ {
+ if (propertySetInfo == null)
+ propertySetInfo= new PropertySetInfo();
+ return propertySetInfo;
+ }
+ //XPropertySet ----------------------------------------------------
+ public Object getPropertyValue(String name) throws UnknownPropertyException, WrappedTargetException
+ {
+ Object ret= null;
+ if (bInDispose || bDisposed)
+ throw new com.sun.star.lang.DisposedException("The component has been disposed already");
+
+ Property prop= getProperty(name);
+ if (prop == null)
+ throw new UnknownPropertyException("The property " + name + " is unknown");
+
+ synchronized (this)
+ {
+ ret= getPropertyValue(prop);
+ }
+ // null must not be returned. Either a void any is returned or an any containing
+ // an interface type and a null reference.
+ if (ret == null)
+ {
+ if (prop.Type.getTypeClass() == TypeClass.INTERFACE)
+ ret= new Any(prop.Type, null);
+ else
+ ret= new Any(new Type(void.class), null);
+ }
+ return ret;
+ }
+
+ //XPropertySet ----------------------------------------------------
+ synchronized public void removePropertyChangeListener(String propName, XPropertyChangeListener listener) throws UnknownPropertyException, WrappedTargetException
+ { // all listeners are automatically released in a dispose call
+ if (!bInDispose && !bDisposed)
+ {
+ if (propName.length() > 0)
+ {
+ Property prop = getProperty(propName);
+ if (prop == null)
+ throw new UnknownPropertyException("Property " + propName + " is unknown");
+ aBoundLC.removeInterface(propName, listener);
+ }
+ else
+ listenerContainer.removeInterface(XPropertyChangeListener.class, listener);
+ }
+ }
+
+ //XPropertySet ----------------------------------------------------
+ synchronized public void removeVetoableChangeListener(String propName, XVetoableChangeListener listener) throws UnknownPropertyException, WrappedTargetException
+ {// all listeners are automatically released in a dispose call
+ if (!bInDispose && !bDisposed)
+ {
+ if (propName.length() > 0)
+ {
+ Property prop = getProperty(propName);
+ if (prop == null)
+ throw new UnknownPropertyException("Property " + propName + " is unknown");
+ aVetoableLC.removeInterface(propName, listener);
+ }
+ else
+ listenerContainer.removeInterface(XVetoableChangeListener.class, listener);
+ }
+ }
+
+ //XPropertySet ----------------------------------------------------
+ /** Sets the value of a property.
+ * The idl description for this interfaces, stipulates that the argument value is an Any. Since a java.lang.Object
+ * reference has the same meaning as an Any this function accepts
+ * java anys (com.sun.star.uno.Any) and all other appropriate objects as arguments. The value argument can be one
+ * of these:
+ * <ul>
+ * <li>java.lang.Boolean</li>
+ * <li>java.lang.Character</li>
+ * <li>java.lang.Byte</li>
+ * <li>java.lang.Short</li>
+ * <li>java.lang.Integer</li>
+ * <li>java.lang.Long</li>
+ * <li>java.lang.Float</li>
+ * <li>java.lang.Double</li>
+ * <li>String</li>
+ * <li>com.sun.star.uno.Type</li>
+ * <li><em>objects which implement UNO interfaces</em></li>
+ * <li><em>arrays which contain elements of the types above</em></li>
+ * <li>com.sun.star.uno.Any containing an instance of one of the above types</li>
+ * </ul>
+ *
+ * Properties can have the attribute com.sun.star.beans.PropertyAttribute.MAYBEVOID, which means that the value
+ * (not the type) can be void. In order to assign a void value to a property one can either pass an Any which
+ * contains a null reference or pass null directly. In both cases the null reference is only accepted if
+ * the PropertyAttribute.MAYBEVOID attribute is set for the property.
+ *
+ * Properties which have the attribute MAYBEVOID set (Property.Attributes) can have a void value. The following
+ * considerations presume that the Property has that attribute set. Further, when mentioning an Any's value we
+ * actually refer to the object returned by Any.getObject.
+ * If the argument <em>value</em> is null, or it is an Any whose value is null (but with a valid Type)
+ * then the member variable used for storing the property's value is set to null.
+ * Therefore those properties can only be stored in objects
+ * and primitive types are not allowed (one can use the wrapper classes instead,e.g. java.lang.Byte) .
+ * If a property's value is kept in a member variable of type Any and that reference is still null
+ * then when setPropertyValue is called with
+ * <em>value</em> = null then the member variable is assigned an Any with type void and a null value.
+ * Or if the argument is an Any with a null value then it is assigned to the member variable.
+ * Further, if the variable already
+ * references an Any and setPropertyValue is called with <em>value</em> = null, then the variable is assigned
+ * a new Any with the same type as the previously referenced Any and with a null value.
+ * @param name The name of the property.
+ * @param value The new value of the property.
+ * * */
+ public void setPropertyValue(String name, Object value) throws UnknownPropertyException,
+ PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ Property prop= getProperty(name);
+ if (prop == null)
+ throw new UnknownPropertyException("Property " + name + " is unknown");
+ setPropertyValue(prop, value);
+ }
+
+ /** Sets the value of a property. It checks if the property's attributes (READONLY,MAYBEVOID), allow that the
+ * new value can be set. It also causes the notification of listeners.
+ * @param prop The property whose value is to be set.
+ * @param value The new value for the property.
+ *
+ * @throws UnknownPropertyException
+ * See com.sun.star.beans.XPropertySet
+ * @throws PropertyVetoException
+ * See com.sun.star.beans.XPropertySet
+ * @throws WrappedTargetException
+ * See com.sun.star.beans.XPropertySet
+ */
+ protected void setPropertyValue(Property prop, Object value) throws UnknownPropertyException,
+ PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ if ((prop.Attributes & PropertyAttribute.READONLY) == PropertyAttribute.READONLY)
+ throw new com.sun.star.beans.PropertyVetoException();
+ // The value may be null only if MAYBEVOID attribute is set
+ boolean bVoidValue;
+ if (value instanceof Any)
+ bVoidValue= ((Any) value).getObject() == null;
+ else
+ bVoidValue= value == null;
+ if (bVoidValue && (prop.Attributes & PropertyAttribute.MAYBEVOID) == 0)
+ throw new com.sun.star.lang.IllegalArgumentException("The property must have a value; the MAYBEVOID attribute is not set!");
+ if (bInDispose || bDisposed)
+ throw new DisposedException("Component is already disposed");
+
+ //Check if the argument is allowed
+ boolean bValueOk;
+ if (value instanceof Any)
+ bValueOk= checkType(((Any) value).getObject());
+ else
+ bValueOk= checkType(value);
+ if (! bValueOk)
+ throw new com.sun.star.lang.IllegalArgumentException("No valid UNO type");
+
+
+ boolean bConversionOk= false;
+ Object[] outConvertedVal= new Object[1];
+ Object[] outOldValue= new Object[1];
+ synchronized (this)
+ {
+ bConversionOk= convertPropertyValue(prop, outConvertedVal, outOldValue, value);
+ }
+
+ //The next step following the conversion is to set the new value of the property. Prior to this
+ // the XVetoableChangeListener s have to be notified.
+ if (bConversionOk)
+ {
+ // If the property is CONSTRAINED, then we must notify XVetoableChangeListener. The listener can throw a com.sun.star.lang.beans.PropertyVetoException which
+ // will cause this method to return (the exception is not caught here).
+ fire( new Property[]{prop}, outConvertedVal, outOldValue, true);
+
+ synchronized (this)
+ {
+ setPropertyValueNoBroadcast(prop, outConvertedVal[0]);
+ }
+ // fire a change event (XPropertyChangeListener, PropertyAttribute.BOUND
+ fire( new Property[]{prop}, outConvertedVal, outOldValue, false);
+ }
+ }
+
+ /** Converts a value in a way so that it is appropriate for storing as a property value, that is
+ * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} can process the value without any further
+ * conversion. This implementation presumes that
+ * the values are stored in member variables of the furthest inheriting class. For example,
+ * class A inherits this class then members of class A
+ * can hold property values. If there is a class B which inherits A then only members of B can hold
+ * property values. The variables must be public. A property must have been registered (e.g. by
+ * {@link #registerProperty(Property, Object)} in order for this method to work. The identifier argument (type Object)
+ * used in the registerProperty methods must
+ * be a String, which is, the name of the member variable that holds the property value.
+ * If one opts to store values differently then one may override
+ * this method, as well as {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and
+ * {@link #getPropertyValue(Property) getPropertyValue(Property)}.
+ * This method is always called as a result of a call to one of the setter methods, such as
+ * {@link #setPropertyValue(String,Object) XPropertySet.setPropertyValue},
+ * {@link #setFastPropertyValue XFastPropertySet.setFastPropertyValue}
+ * and {@link #setPropertyValues XMultiPropertySet.setPropertyValues}.
+ * If this method fails, that is, it returns false or throws an exception, then no listeners are notified and the
+ * property value, that was intended to be changed, remains untouched.
+ *
+ * This method does not have to deal with property attributes, such as
+ * PropertyAttribute.READONLY or PropertyAttribute.MAYBEVOID. The processing of these attributes occurs
+ * in the calling methods.
+ *
+ * Only if this method returns successfully further processing, such
+ * as listener notification and finally the modification of the property's value, will occur.
+ *
+ * The actual modification of a property's value is done by {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast}
+ * which is called subsequent to convertPropertyValue.
+ *<p>
+ * This method converts values by help of the com.sun.star.uno.AnyConverter which only does a few widening
+ * conversions on integer types and floating point types. For example, there is the property PropA with a Type equivalent
+ * to int.class and the
+ * value of the property is to be stored in a member variable of type int with name intProp. Then setPropertyValue is
+ * called:
+ * <pre>
+ * set.setPropertyValue( "PropA", Byte.valueOf( (byte)111));
+ * </pre>
+ * At some point setPropertyValue will call convertPropertyValue and pass in the Byte object. Since we allow
+ * that Byte values can be used with the property and know that the value is to be stored in intProp (type int)
+ * we convert the Byte object into an Integer object which is then returned in the out-parameter <em>newVal</em>. This
+ * conversion is actually performed by the AnyConverter. Later
+ * the setPropertyValueNoBroadcast is called with that Integer object and the int value can be easily extracted
+ * from the object and be assigned to the member intProp.
+ * <p>
+ * The method handles Any arguments the same as Object arguments. That is, the <em>setVal</em> argument can
+ * be a java.lang.Boolean or a com.sun.star.uno.Any containing a java.lang.Boolean. Likewise, a member
+ * containing a property value can be a com.sun.star.uno.Any or a java.lang.Object.
+ * Then, no conversion is necessary, since they can hold all possible values. However, if
+ * the member is an Object and <em>setVal</em> is an Any then the object contained in the any is assigned to
+ * the member. The extra type information which exists as Type object in the Any will get lost. If this is not
+ * intended then use an Any variable rather than an Object.
+ *
+ * If a member is an Object or Any and the argument <em>setVal</em> is an Object, other than String or array,
+ * then it is presumed to be a UNO object and queried for XInterface. If successful, the out-param <em>newVal</em>
+ * returns the XInterface.
+ *
+ * If a member is a UNO interface, then <em>setVal</em> is queried for this interface and the result is returned.
+ * If <em>setVal</em> is null then <em>newVal</em> will be null too after return.
+ * <p>
+ * If a property value is stored using a primitive type the out-parameters
+ * <em>curVal</em> and <em>newVal</em> contain the respective wrapper class (e.g.java.lang.Byte, etc.).
+ * curVal is used in calls to the XVetoableChangeListener and XPropertyChangeListener.
+ *
+ * @param property - in-param property for which the data is to be converted.
+ * @param newVal - out-param which contains the converted value on return.
+ * @param curVal - out-param the current value of the property. It is used in calls to the
+ * XVetoableChangeListener and XPropertyChangeListener.
+ * @param setVal - in-param. The value that is to be converted so that it matches Property and the internally used
+ * dataformat for that property.
+ * @return true - Conversion was successful. <em>newVal</em> contains a valid value for the property. false -
+ * conversion failed for some reason.
+ *
+ * @throws UnknownPropertyException
+ * See com.sun.star.beans.XPropertySet
+ * @throws com.sun.star.lang.IllegalArgumentException The value provided is unfit for the property.
+ * @throws com.sun.star.lang.WrappedTargetException - An exception occurred during the conversion, that is to be made known
+ * to the caller.
+ */
+ protected boolean convertPropertyValue(Property property, Object[] newVal, Object[]curVal, Object setVal)
+ throws com.sun.star.lang.IllegalArgumentException, WrappedTargetException, UnknownPropertyException
+ {
+ boolean ret= true;
+ try
+ {
+ // get the member name
+ String sMember= (String) getPropertyId(property);
+ if (sMember != null)
+ {
+ // use reflection to obtain the field that holds the property value
+ // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
+ // also get inherited fields, but only those which are public.
+ Field propField= getClass().getDeclaredField(sMember);
+ if (propField != null)
+ {
+ curVal[0]= propField.get(this);
+ Class memberClass= propField.getType();
+
+ // MAYBEVOID: if setVal == null or it is an Any and getObject returns null, then a void value is to be set
+ // This works only if there are no primitive types. For those we use the respective wrapper classes.
+ // In this implementation, a null reference means void value.
+ boolean bVoidValue= false;
+ boolean bAnyVal= setVal instanceof Any;
+ if (bAnyVal)
+ bVoidValue= ((Any) setVal).getObject() == null;
+ else
+ bVoidValue= setVal == null;
+ if (bVoidValue && memberClass.isPrimitive())
+ throw new com.sun.star.lang.IllegalArgumentException("The implementation does not support the MAYBEVOID attribute for this property");
+
+ Object convObj= null;
+ //The member that keeps the value of the Property is an Any. It can contain all possible
+ //types, therefore a conversion is not necessary.
+ if (memberClass.equals(Any.class))
+ {
+ if (bAnyVal)
+ //parameter setVal is also an Any and can be used without further processing
+ convObj= setVal;
+ else
+ {
+ // Parameter setVal is not an Any. We need to construct an Any that contains
+ // the argument setVal.
+ // If setVal is an interface implementation then, we cannot construct the
+ // Any with setVal.getClass(), because the Any.Type._typeClass would be TypeClass.UNKNOWN.
+ // We try to get an XInterface of setVal and set an XInterface type.
+ if (setVal instanceof XInterface)
+ {
+ XInterface xint= UnoRuntime.queryInterface(XInterface.class, setVal);
+ if (xint != null)
+ convObj= new Any(new Type(XInterface.class), xint);
+ }
+ // The member is an any, and the past in argument was null reference (MAYBEVOID is set)
+ else if (setVal == null)
+ {
+ // if the any member is still null we create a void any
+ if (curVal[0] == null)
+ convObj= new Any(new Type(), null);
+ else
+ {
+ //otherwise we create an Any with the same type as a value of null;
+ convObj= new Any( ((Any)curVal[0]).getType(), null);
+ }
+ }
+ else
+ convObj= new Any(new Type(setVal.getClass()), setVal);
+ }
+ }
+ else
+ convObj= convert(memberClass, setVal);
+ newVal[0]= convObj;
+ }
+ }
+ else
+ throw new UnknownPropertyException("Property " + property.Name + " is unknown");
+ }
+ catch (java.lang.NoSuchFieldException e)
+ {
+ throw new WrappedTargetException(e, "Field does not exist", this, e);
+ }
+ catch (java.lang.IllegalAccessException e)
+ {
+ throw new WrappedTargetException(e, "", this ,e);
+ }
+ return ret;
+ }
+
+ private boolean checkType(Object obj)
+ {
+ return obj == null
+ || obj instanceof Boolean
+ || obj instanceof Character
+ || obj instanceof Number
+ || obj instanceof String
+ || obj instanceof XInterface
+ || obj instanceof Type
+ || obj instanceof com.sun.star.uno.Enum
+ || obj.getClass().isArray();
+ }
+
+ // Param object can be an Any or other object. If obj is null then the return value is null
+ private Object convert( Class cl, Object obj) throws com.sun.star.lang.IllegalArgumentException
+ {
+ Object retVal= null;
+ //The member that keeps the value of the Property is an Object.Objects are similar to Anys in that they can
+ // hold all types.
+ if (obj == null || (obj instanceof Any && ((Any) obj).getObject() == null))
+ {}
+ else if(cl.equals(Object.class))
+ {
+ if (obj instanceof Any)
+ obj= ((Any) obj).getObject();
+ retVal= obj;
+ }
+ else if(cl.equals(boolean.class))
+ retVal= Boolean.valueOf(AnyConverter.toBoolean(obj));
+ else if (cl.equals(char.class))
+ retVal= new Character(AnyConverter.toChar(obj));
+ else if (cl.equals(byte.class))
+ retVal= Byte.valueOf(AnyConverter.toByte(obj));
+ else if (cl.equals(short.class))
+ retVal= Short.valueOf(AnyConverter.toShort(obj));
+ else if (cl.equals(int.class))
+ retVal= Integer.valueOf(AnyConverter.toInt(obj));
+ else if (cl.equals(long.class))
+ retVal= Long.valueOf(AnyConverter.toLong(obj));
+ else if (cl.equals(float.class))
+ retVal= new Float(AnyConverter.toFloat(obj));
+ else if (cl.equals(double.class))
+ retVal= new Double(AnyConverter.toDouble(obj));
+ else if (cl.equals(String.class))
+ retVal= AnyConverter.toString(obj);
+ else if (cl.isArray())
+ retVal= AnyConverter.toArray(obj);
+ else if (cl.equals(Type.class))
+ retVal= AnyConverter.toType(obj);
+ else if (cl.equals(Boolean.class))
+ retVal= Boolean.valueOf(AnyConverter.toBoolean(obj));
+ else if (cl.equals(Character.class))
+ retVal= new Character(AnyConverter.toChar(obj));
+ else if (cl.equals(Byte.class))
+ retVal= Byte.valueOf(AnyConverter.toByte(obj));
+ else if (cl.equals(Short.class))
+ retVal= Short.valueOf(AnyConverter.toShort(obj));
+ else if (cl.equals(Integer.class))
+ retVal= Integer.valueOf(AnyConverter.toInt(obj));
+ else if (cl.equals(Long.class))
+ retVal= Long.valueOf(AnyConverter.toLong(obj));
+ else if (cl.equals(Float.class))
+ retVal= new Float(AnyConverter.toFloat(obj));
+ else if (cl.equals(Double.class))
+ retVal= new Double(AnyConverter.toDouble(obj));
+ else if (XInterface.class.isAssignableFrom(cl))
+ retVal= AnyConverter.toObject(new Type(cl), obj);
+ else if (com.sun.star.uno.Enum.class.isAssignableFrom(cl))
+ retVal= AnyConverter.toObject(new Type(cl), obj);
+ else
+ throw new com.sun.star.lang.IllegalArgumentException("Could not convert the argument");
+ return retVal;
+ }
+
+ /** Sets the value of a property. In this implementation property values are stored in member variables
+ * (see {@link #convertPropertyValue convertPropertyValue} Notification of property listeners
+ * does not occur in this method. By overriding this method one can take full control about how property values
+ * are stored. But then, the {@link #convertPropertyValue convertPropertyValue} and
+ * {@link #getPropertyValue(Property)} must be overridden too.
+ *
+ * A Property with the MAYBEVOID attribute set, is stored as null value. Therefore the member variable must be
+ * an Object in order to make use of the property attribute. An exception is Any. The Any variable can be initially null, but
+ * once it is set the reference will not become null again. If the value is to be set to
+ * void then a new Any will be stored
+ * with a valid type but without a value (i.e. Any.getObject returns null).
+ * If a property has the READONLY attribute set, and one of the setter methods, such as setPropertyValue, has been
+ * called, then this method is not going to be called.
+ * @param property the property for which the new value is set
+ * @param newVal the new value for the property.
+ * @throws com.sun.star.lang.WrappedTargetException An exception, which has to be made known to the caller,
+ * occurred during the setting of the value.
+ */
+ protected void setPropertyValueNoBroadcast(Property property, Object newVal)
+ throws WrappedTargetException
+ {
+ try
+ {
+ // get the member name
+ String sMember= (String) getPropertyId(property);
+ if (sMember != null)
+ {
+ // use reflection to obtain the field that holds the property value
+ // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
+ // also get inherited fields, but only those which are public.
+ Field propField= getClass().getDeclaredField(sMember);
+ if (propField != null)
+ propField.set(this, newVal);
+ }
+ }
+ catch(java.lang.Exception e)
+ {
+ throw new WrappedTargetException(e, "PropertySet.setPropertyValueNoBroadcast", this, e);
+ }
+ }
+ /** Retrieves the value of a property. This implementation presumes that the values are stored in member variables
+ * of the furthest inheriting class (see {@link #convertPropertyValue convertPropertyValue}) and that the
+ * variables are public. The property must have
+ * been registered, for example by {@link #registerProperty(Property, Object)}. The identifier Object argument
+ * must have been a String which was the name of the member variable holding the property value.
+ * When properties are to be stored differently one has to override this method as well as
+ * {@link #convertPropertyValue} and {@link #setPropertyValueNoBroadcast}. <br>
+ * If a value is stored in a variable of a primitive type then this method returns an instance of the respective
+ * wrapper class (e.g. java.lang.Boolean).
+ * @param property The property for which the value is to be retrieved.
+ * @return The value of the property.
+ */
+ protected Object getPropertyValue(Property property)
+ {
+ Object ret= null;
+ try
+ {
+ // get the member name
+ String sMember= (String) getPropertyId(property);
+ if (sMember != null)
+ {
+ // use reflection to obtain the field that holds the property value
+ // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
+ // also get inherited fields, but only those which are public.
+ Field propField= getClass().getDeclaredField(sMember);
+ if (propField != null)
+ ret= propField.get(this);
+ }
+ }
+ catch(java.lang.NoSuchFieldException e)
+ {
+ throw new java.lang.RuntimeException(e);
+ }
+ catch(java.lang.IllegalAccessException e)
+ {
+ throw new java.lang.RuntimeException(e);
+ }
+ return ret;
+ }
+
+ /**
+ * This method fires events to XPropertyChangeListener,XVetoableChangeListener and
+ * XPropertiesChangeListener event sinks.
+ * To distinguish what listeners are to be called the argument <em>bVetoable</em> is to be set to true if
+ * a XVetoableChangeListener is meant. For XPropertyChangeListener and XPropertiesChangeListener
+ * it is to be set to false.
+ *
+ * @param properties Properties which will be or have been affected.
+ * @param newValues the new values of the properties.
+ * @param oldValues the old values of the properties.
+ * @param bVetoable true means fire to VetoableChangeListener, false means fire to
+ * XPropertyChangedListener and XMultiPropertyChangedListener.
+ *
+ * @throws PropertyVetoException
+ * if a vetoable listener throws it.
+ */
+ protected void fire(
+ Property[] properties,
+ Object[] newValues,
+ Object[] oldValues,
+ boolean bVetoable ) throws PropertyVetoException
+ {
+ // Only fire, if one or more properties changed
+ int nNumProps= properties.length;
+ if (nNumProps > 0)
+ {
+ PropertyChangeEvent[] arEvts= new PropertyChangeEvent[nNumProps];
+ int nAffectedProps= 0;
+ // Loop over all changed properties to fill the event struct
+ for (int i= 0; i < nNumProps; i++)
+ {
+ if ((bVetoable && (properties[i].Attributes & PropertyAttribute.CONSTRAINED) > 0)
+ || (!bVetoable && (properties[i].Attributes & PropertyAttribute.BOUND) > 0))
+ {
+ arEvts[i]= new PropertyChangeEvent(this, properties[i].Name, false,
+ properties[i].Handle, oldValues[i], newValues[i]);
+ nAffectedProps++;
+ }
+ }
+ // fire the events for all changed properties
+ for (int i= 0; i < nAffectedProps; i++)
+ {
+ // get the listener container for the property name
+ InterfaceContainer lc;
+ if (bVetoable)
+ lc= aVetoableLC.getContainer(arEvts[i].PropertyName);
+ else
+ lc= aBoundLC.getContainer(arEvts[i].PropertyName);
+ Iterator it = lc != null ? lc.iterator() : null;
+ if (it != null)
+ {
+ while( it.hasNext())
+ {
+ Object listener= it.next();
+ if (bVetoable)
+ ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]);
+ else
+ ((XPropertyChangeListener) listener).propertyChange(arEvts[i]);
+ }
+ }
+ // broadcast to all listeners with "" property name
+ if(bVetoable)
+ lc= listenerContainer.getContainer(XVetoableChangeListener.class);
+ else
+ lc= listenerContainer.getContainer(XPropertyChangeListener.class);
+ it = lc != null ? lc.iterator() : null;
+ if (it != null)
+ {
+ while(it.hasNext() )
+ {
+ Object listener= it.next();
+ if( bVetoable ) // fire change Events?
+ ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]);
+ else
+ ((XPropertyChangeListener) listener).propertyChange(arEvts[i]);
+ }
+ }
+ }
+ // fire at XPropertiesChangeListeners
+ // if nAffectedProps == 0 then there are no BOUND properties
+ if (!bVetoable && nAffectedProps > 0)
+ {
+
+ PropertyChangeEvent[] arReduced= new PropertyChangeEvent[nAffectedProps];
+ System.arraycopy(arEvts, 0, arReduced, 0, nAffectedProps);
+ InterfaceContainer lc= listenerContainer.getContainer(XPropertiesChangeListener.class);
+ Iterator it = lc != null ? lc.iterator() : null;
+ if (it != null)
+ {
+ while (it.hasNext())
+ {
+ XPropertiesChangeListener listener = (XPropertiesChangeListener) it.next();
+ // fire the whole event sequence to the XPropertiesChangeListener's
+ listener.propertiesChange( arEvts );
+ }
+ }
+ }
+ }
+ }
+ // XFastPropertySet--------------------------------------------------------------------------------
+ public void setFastPropertyValue(int nHandle, Object aValue ) throws UnknownPropertyException,
+ PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ Property prop= getPropertyByHandle(nHandle);
+ if (prop == null)
+ throw new UnknownPropertyException(" The property with handle : " + nHandle +" is unknown");
+ setPropertyValue(prop, aValue);
+ }
+
+ // XFastPropertySet --------------------------------------------------------------------------------
+ public Object getFastPropertyValue(int nHandle ) throws UnknownPropertyException,
+ WrappedTargetException
+ {
+ Property prop= getPropertyByHandle(nHandle);
+ if (prop == null)
+ throw new UnknownPropertyException("The property with handle : " + nHandle + " is unknown");
+ return getPropertyValue(prop);
+ }
+
+ // XMultiPropertySet -----------------------------------------------------------------------------------
+ public void addPropertiesChangeListener(String[] propNames, XPropertiesChangeListener listener)
+ {
+ listenerContainer.addInterface(XPropertiesChangeListener.class, listener);
+ }
+
+ // XMultiPropertySet -----------------------------------------------------------------------------------
+ public void firePropertiesChangeEvent(String[] propNames, XPropertiesChangeListener listener)
+ {
+ // Build the events.
+ PropertyChangeEvent[] arEvents= new PropertyChangeEvent[propNames.length];
+ int eventCount= 0;
+ // get a snapshot of the current property values
+ synchronized (this)
+ {
+ for (int i= 0; i < propNames.length; i++)
+ {
+ Property prop= getProperty(propNames[i]);
+ if (prop != null)
+ {
+ Object value= null;
+ try
+ {
+ value= getPropertyValue(prop);
+ }
+ catch(Exception e)
+ {
+ continue;
+ }
+ arEvents[eventCount]= new PropertyChangeEvent(this, prop.Name,
+ false, prop.Handle, value, value);
+ eventCount++;
+ }
+ }
+ }
+
+ // fire events from unsynchronized section so as to prevent deadlocks
+ if (eventCount > 0)
+ {
+ // Reallocate the array of the events if necessary
+ if (arEvents.length != eventCount)
+ {
+ PropertyChangeEvent[] arPropsTmp= new PropertyChangeEvent[eventCount];
+ System.arraycopy(arEvents, 0, arPropsTmp, 0, eventCount);
+ arEvents= arPropsTmp;
+ }
+ listener.propertiesChange(arEvents);
+ }
+ }
+ // XMultiPropertySet -----------------------------------------------------------------------------------
+ /** If a value for a property could not be retrieved then the respective element in the returned
+ * array has the value null.
+ */
+ public Object[] getPropertyValues(String[] propNames)
+ {
+ Object[] arValues= new Object[propNames.length];
+ synchronized (this)
+ {
+ for (int i= 0; i < propNames.length; i++)
+ {
+ Object value= null;
+ try
+ {
+ value= getPropertyValue(propNames[i]);
+ }
+ catch (Exception e)
+ {
+ }
+ arValues[i]= value;
+ }
+ }
+ return arValues;
+ }
+ // XMultiPropertySet -----------------------------------------------------------------------------------
+ public void removePropertiesChangeListener(XPropertiesChangeListener xPropertiesChangeListener)
+ {
+ listenerContainer.removeInterface(XPropertiesChangeListener.class, xPropertiesChangeListener);
+ }
+ // XMultiPropertySet -----------------------------------------------------------------------------------
+ /** If the array of property names contains an unknown property then it will be ignored.
+ */
+ public void setPropertyValues(String[] propNames, Object[] values) throws PropertyVetoException, com.sun.star.lang.IllegalArgumentException, com.sun.star.lang.WrappedTargetException
+ {
+ for (int i= 0; i < propNames.length; i++)
+ {
+ try
+ {
+ setPropertyValue(propNames[i], values[i]);
+ }
+ catch (UnknownPropertyException e)
+ {
+ continue;
+ }
+
+ }
+ }
+
+ private class PropertySetInfo implements XPropertySetInfo
+ {
+ public com.sun.star.beans.Property[] getProperties()
+ {
+ return PropertySet.this.getProperties();
+ }
+
+ public com.sun.star.beans.Property getPropertyByName(String name) throws UnknownPropertyException
+ {
+ return getProperty(name);
+ }
+
+ public boolean hasPropertyByName(String name)
+ {
+ return getProperty(name) != null;
+ }
+
+ }
+}
+
+
+
+
+
diff --git a/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java b/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java
new file mode 100644
index 000000000..0c050d676
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java
@@ -0,0 +1,1111 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+
+import com.sun.star.beans.Property;
+import com.sun.star.beans.PropertyAttribute;
+import com.sun.star.beans.PropertyChangeEvent;
+import com.sun.star.beans.PropertyState;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.beans.UnknownPropertyException;
+import com.sun.star.beans.XPropertyChangeListener;
+import com.sun.star.beans.XPropertySetInfo;
+import com.sun.star.beans.XVetoableChangeListener;
+import com.sun.star.container.NoSuchElementException;
+import com.sun.star.container.XHierarchicalNameAccess;
+import com.sun.star.lang.DisposedException;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.lang.WrappedTargetRuntimeException;
+import com.sun.star.reflection.XCompoundTypeDescription;
+import com.sun.star.reflection.XIdlClass;
+import com.sun.star.reflection.XIdlField2;
+import com.sun.star.reflection.XIndirectTypeDescription;
+import com.sun.star.reflection.XInterfaceAttributeTypeDescription2;
+import com.sun.star.reflection.XInterfaceMemberTypeDescription;
+import com.sun.star.reflection.XInterfaceTypeDescription2;
+import com.sun.star.reflection.XStructTypeDescription;
+import com.sun.star.reflection.XTypeDescription;
+import com.sun.star.reflection.theCoreReflection;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.AnyConverter;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.uno.XInterface;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ A helper mixin to implement certain UNO interfaces related to property set
+ handling on top of the attributes of a given UNO interface type.
+
+ <p>A client will mix in this class by keeping a reference to an instance of
+ this class, and forwarding all methods of (a subset of the interfaces)
+ <code>com.sun.star.beans.XPropertySet</code>,
+ <code>com.sun.star.beans.XFastPropertySet</code>, and
+ <code>com.sun.star.beans.XPropertyAccess</code> to it.</p>
+
+ <p>Client code should not use the monitors associated with instances of this
+ class, as they are used for internal purposes.</p>
+
+ @since UDK 3.2
+*/
+public final class PropertySetMixin {
+ /**
+ The constructor.
+
+ @param context the component context used by this instance; must not be
+ null, and must supply the
+ <code>com.sun.star.reflection.theCoreReflection</code> and
+ <code>com.sun.star.reflection.theTypeDescriptionManager</code> singletons
+
+ @param object the client UNO object into which this instance is mixed in;
+ must not be null, and must support the given <code>type</code>
+
+ @param type the UNO interface type whose attributes are mapped to
+ properties; must not be null, and must represent a UNO interface type
+
+ @param absentOptional a list of optional properties that are not present,
+ and should thus not be visible via
+ <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>,
+ <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>,
+ <code>com.sun.star.beans.XPropertySet.removePropertyChangeListener<!--
+ --></code>,
+ <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>,
+ and <code>com.sun.star.beans.XPropertySet.<!--
+ -->removeVetoableChangeListener</code>; null is treated the same as an
+ empty list; if non-null, the given array must not be modified after it is
+ passed to this constructor. For consistency reasons, the given
+ <code>absentOptional</code> should only contain the names of attributes
+ that represent optional properties that are not present (that is, the
+ attribute getters and setters always throw a
+ <code>com.sun.star.beans.UnknownPropertyException</code>), and should
+ contain each such name only once. If an optional property is not present
+ (that is, the corresponding attribute getter and setter always throw a
+ <code>com.sun.star.beans.UnknownPropertyException</code>) but is not
+ contained in the given <code>absentOptional</code>, then it will be
+ visible via
+ <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code> as a
+ <code>com.sun.star.beans.Property</code> with a set
+ <code>com.sun.star.beans.PropertyAttribute.OPTIONAL</code>. If the given
+ <code>object</code> does not implement
+ <code>com.sun.star.beans.XPropertySet</code>, then the given
+ <code>absentOptional</code> is effectively ignored and can be null or
+ empty.
+ */
+ public PropertySetMixin(
+ XComponentContext context, XInterface object, Type type,
+ String[] absentOptional)
+ {
+ this.context = context;
+ this.object = object;
+ this.type = type;
+ this.absentOptional = absentOptional;
+ idlClass = getReflection(type.getTypeName());
+ XTypeDescription ifc;
+ try {
+ XHierarchicalNameAccess xhna = UnoRuntime.queryInterface(
+ XHierarchicalNameAccess.class,
+ context.getValueByName(
+ "/singletons/com.sun.star.reflection."
+ + "theTypeDescriptionManager"));
+ ifc = UnoRuntime.queryInterface(
+ XTypeDescription.class,
+ xhna.getByHierarchicalName(type.getTypeName()));
+ } catch (NoSuchElementException e) {
+ throw new RuntimeException(e);
+ }
+ HashMap<String,PropertyData> map = new HashMap<String,PropertyData>();
+ ArrayList<String> handleNames = new ArrayList<String>();
+ initProperties(ifc, map, handleNames, new HashSet<String>());
+ properties = map;
+ handleMap = handleNames.toArray(new String[handleNames.size()]);
+ }
+
+ /**
+ A method used by clients when implementing UNO interface type attribute
+ setter functions.
+
+ <p>First, this method checks whether this instance has already been
+ disposed (see {@link #dispose}), and throws a
+ <code>com.sun.star.beans.DisposedException</code> if applicable. For a
+ constrained attribute (whose setter can explicitly raise
+ <code>com.sun.star.beans.PropertyVetoException</code>), this method
+ notifies any <code>com.sun.star.beans.XVetoableChangeListener</code>s.
+ For a bound attribute, this method modifies the passed-in
+ <code>bound</code> so that it can afterwards be used to notify any
+ <code>com.sun.star.beans.XPropertyChangeListener</code>s. This method
+ should be called before storing the new attribute value, and
+ <code>bound.notifyListeners()</code> should be called exactly once after
+ storing the new attribute value (in case the attribute is bound;
+ otherwise, calling <code>bound.notifyListeners()</code> is ignored).
+ Furthermore, <code>bound.notifyListeners()</code> and this method have to
+ be called from the same thread.</p>
+
+ @param propertyName the name of the property (which is the same as the
+ name of the attribute that is going to be set)
+
+ @param oldValue the property value corresponding to the old attribute
+ value. This is only used as
+ <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code>, which is
+ rather useless, anyway (see &ldquo;Using the Observer Pattern&rdquo; in
+ <a href="http://tools.openoffice.org/CodingGuidelines.sxw">
+ <cite>OpenOffice.org Coding Guidelines</cite></a>). If the attribute
+ that is going to be set is neither bound nor constrained, or if
+ <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code> should not
+ be set, {@link Any#VOID} can be used instead.
+
+ @param newValue the property value corresponding to the new
+ attribute value. This is only used as
+ <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code>, which is
+ rather useless, anyway (see &ldquo;Using the Observer Pattern&rdquo; in
+ <a href="http://tools.openoffice.org/CodingGuidelines.sxw">
+ <cite>OpenOffice.org Coding Guidelines</cite></a>), <em>unless</em> the
+ attribute that is going to be set is constrained. If the attribute
+ that is going to be set is neither bound nor constrained, or if it is
+ only bound but
+ <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code> should not
+ be set, {@link Any#VOID} can be used instead.
+
+ @param bound a reference to a fresh {@link BoundListeners} instance
+ (which has not been passed to this method before, and on which
+ {@link BoundListeners#notifyListeners} has not yet been called); may only
+ be null if the attribute that is going to be set is not bound
+
+ @throws PropertyVetoException if a vetoable listener throws it.
+ */
+ public void prepareSet(
+ String propertyName, Object oldValue, Object newValue,
+ BoundListeners bound)
+ throws PropertyVetoException
+ {
+ // assert properties.get(propertyName) != null;
+ Property p = properties.get(propertyName).property;
+ ArrayList<XVetoableChangeListener> specificVeto = null;
+ ArrayList<XVetoableChangeListener> unspecificVeto = null;
+ synchronized (this) {
+ if (disposed) {
+ throw new DisposedException("disposed", object);
+ }
+ if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) {
+ ArrayList<XVetoableChangeListener> o = vetoListeners.get(propertyName);
+ if (o != null) {
+ specificVeto = new ArrayList<XVetoableChangeListener>(o);
+ }
+ o = vetoListeners.get("");
+ if (o != null) {
+ unspecificVeto = new ArrayList<XVetoableChangeListener>(o);
+ }
+ }
+ if ((p.Attributes & PropertyAttribute.BOUND) != 0) {
+ // assert bound != null;
+ ArrayList<XPropertyChangeListener> o = boundListeners.get(propertyName);
+ if (o != null) {
+ bound.specificListeners = new ArrayList<XPropertyChangeListener>(o);
+ }
+ o = boundListeners.get("");
+ if (o != null) {
+ bound.unspecificListeners = new ArrayList<XPropertyChangeListener>(o);
+ }
+ }
+ }
+ if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) {
+ PropertyChangeEvent event = new PropertyChangeEvent(
+ object, propertyName, false, p.Handle, oldValue, newValue);
+ if (specificVeto != null) {
+ for (Iterator<XVetoableChangeListener> i = specificVeto.iterator(); i.hasNext();) {
+ try {
+ i.next().vetoableChange(event);
+ } catch (DisposedException e) {}
+ }
+ }
+ if (unspecificVeto != null) {
+ for (Iterator<XVetoableChangeListener> i = unspecificVeto.iterator(); i.hasNext();) {
+ try {
+ i.next().vetoableChange(event);
+ } catch (DisposedException e) {}
+ }
+ }
+ }
+ if ((p.Attributes & PropertyAttribute.BOUND) != 0) {
+ // assert bound != null;
+ bound.event = new PropertyChangeEvent(
+ object, propertyName, false, p.Handle, oldValue, newValue);
+ }
+ }
+
+ /**
+ A simplified version of {@link #prepareSet(String, Object, Object,
+ PropertySetMixin.BoundListeners)}.
+
+ <p>This method is useful for attributes that are not constrained.</p>
+
+ @param propertyName the name of the property (which is the same as the
+ name of the attribute that is going to be set)
+
+ @param bound a reference to a fresh {@link BoundListeners} instance
+ (which has not been passed to this method before, and on which
+ {@link BoundListeners#notifyListeners} has not yet been called); may only
+ be null if the attribute that is going to be set is not bound
+ */
+ public void prepareSet(String propertyName, BoundListeners bound) {
+ try {
+ prepareSet(propertyName, Any.VOID, Any.VOID, bound);
+ } catch (PropertyVetoException e) {
+ throw new RuntimeException("unexpected " + e);
+ }
+ }
+
+ /**
+ Marks this instance as being disposed.
+
+ <p>See <code>com.sun.star.lang.XComponent</code> for the general concept
+ of disposing UNO objects. On the first call to this method, all
+ registered listeners
+ (<code>com.sun.star.beans.XPropertyChangeListener</code>s and
+ <code>com.sun.star.beans.XVetoableChangeListener</code>s) are notified of
+ the disposing source. Any subsequent calls to this method are
+ ignored.</p>
+ */
+ public void dispose() {
+ HashMap<String,ArrayList<XPropertyChangeListener>> bound;
+ HashMap<String,ArrayList<XVetoableChangeListener>> veto;
+ synchronized (this) {
+ bound = boundListeners;
+ boundListeners = null;
+ veto = vetoListeners;
+ vetoListeners = null;
+ disposed = true;
+ }
+ EventObject event = new EventObject(object);
+ if (bound != null) {
+ for (Iterator<ArrayList<XPropertyChangeListener>> i = bound.values().iterator(); i.hasNext();) {
+ for (Iterator<XPropertyChangeListener> j = i.next().iterator(); j.hasNext();)
+ {
+ j.next().disposing(event);
+ }
+ }
+ }
+ if (veto != null) {
+ for (Iterator<ArrayList<XVetoableChangeListener>> i = veto.values().iterator(); i.hasNext();) {
+ for (Iterator<XVetoableChangeListener> j = i.next().iterator(); j.hasNext();)
+ {
+ j.next().disposing(event);
+ }
+ }
+ }
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>.
+ @return See com.sun.star.beans.XPropertySet
+ */
+ public XPropertySetInfo getPropertySetInfo() {
+ return new Info(properties);
+ }
+
+ /**
+ Implements <code>com.sun.star.beans.XPropertySet.setPropertyValue</code>.
+ @param propertyName
+ See com.sun.star.beans.XPropertySet
+ @param value
+ See com.sun.star.beans.XPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertySet
+ @throws PropertyVetoException
+ See com.sun.star.beans.XPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertySet
+ */
+ public void setPropertyValue(String propertyName, Object value)
+ throws UnknownPropertyException, PropertyVetoException,
+ com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ setProperty(propertyName, value, false, false, (short) 1);
+ }
+
+ /**
+ Implements <code>com.sun.star.beans.XPropertySet.getPropertyValue</code>.
+ @param propertyName
+ See com.sun.star.beans.XPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertySet
+ @return
+ See com.sun.star.beans.XPropertySet
+ */
+ public Object getPropertyValue(String propertyName)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ return getProperty(propertyName, null);
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>.
+
+ <p>If a listener is added more than once, it will receive all relevant
+ notifications multiple times.</p>
+
+ @param propertyName
+ See com.sun.star.beans.XPropertySet
+ @param listener
+ See com.sun.star.beans.XPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertySet
+ */
+ public void addPropertyChangeListener(
+ String propertyName, XPropertyChangeListener listener)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ // assert listener != null;
+ checkUnknown(propertyName);
+ boolean disp;
+ synchronized (this) {
+ disp = disposed;
+ if (!disp) {
+ ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName);
+ if (v == null) {
+ v = new ArrayList<XPropertyChangeListener>();
+ boundListeners.put(propertyName, v);
+ }
+ v.add(listener);
+ }
+ }
+ if (disp) {
+ listener.disposing(new EventObject(object));
+ }
+ }
+
+ /**
+ Implements <code>
+ com.sun.star.beans.XPropertySet.removePropertyChangeListener</code>.
+
+ @param propertyName
+ See com.sun.star.beans.XPropertySet
+ @param listener
+ See com.sun.star.beans.XPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertySet
+ */
+ public void removePropertyChangeListener(
+ String propertyName, XPropertyChangeListener listener)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ // assert listener != null;
+ checkUnknown(propertyName);
+ synchronized (this) {
+ if (boundListeners != null) {
+ ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName);
+ if (v != null) {
+ v.remove(listener);
+ }
+ }
+ }
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>.
+
+ <p>If a listener is added more than once, it will receive all relevant
+ notifications multiple times.</p>
+
+ @param propertyName
+ See com.sun.star.beans.XPropertySet
+ @param listener
+ See com.sun.star.beans.XPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertySet
+ */
+ public void addVetoableChangeListener(
+ String propertyName, XVetoableChangeListener listener)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ // assert listener != null;
+ checkUnknown(propertyName);
+ boolean disp;
+ synchronized (this) {
+ disp = disposed;
+ if (!disp) {
+ ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName);
+ if (v == null) {
+ v = new ArrayList<XVetoableChangeListener>();
+ vetoListeners.put(propertyName, v);
+ }
+ v.add(listener);
+ }
+ }
+ if (disp) {
+ listener.disposing(new EventObject(object));
+ }
+ }
+
+ /**
+ Implements <code>
+ com.sun.star.beans.XPropertySet.removeVetoableChangeListener</code>.
+
+ @param propertyName
+ See com.sun.star.beans.XPropertySet
+ @param listener
+ See com.sun.star.beans.XPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertySet
+ */
+ public void removeVetoableChangeListener(
+ String propertyName, XVetoableChangeListener listener)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ // assert listener != null;
+ checkUnknown(propertyName);
+ synchronized (this) {
+ if (vetoListeners != null) {
+ ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName);
+ if (v != null) {
+ v.remove(listener);
+ }
+ }
+ }
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XFastPropertySet.setFastPropertyValue</code>.
+
+ @param handle
+ See com.sun.star.beans.XFastPropertySet
+ @param value
+ See com.sun.star.beans.XFastPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XFastPropertySet
+ @throws PropertyVetoException
+ See com.sun.star.beans.XFastPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XFastPropertySet
+ */
+ public void setFastPropertyValue(int handle, Object value)
+ throws UnknownPropertyException, PropertyVetoException,
+ com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ setProperty(translateHandle(handle), value, false, false, (short) 1);
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XFastPropertySet.getFastPropertyValue</code>.
+
+ @param handle
+ See com.sun.star.beans.XFastPropertySet
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XFastPropertySet
+ @throws WrappedTargetException
+ See com.sun.star.beans.XFastPropertySet
+ @return
+ See com.sun.star.beans.XFastPropertySet
+ */
+ public Object getFastPropertyValue(int handle)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ return getProperty(translateHandle(handle), null);
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XPropertyAccess.getPropertyValues</code>.
+
+ @return
+ See com.sun.star.beans.XPropertyAccess
+ */
+ public PropertyValue[] getPropertyValues() {
+ PropertyValue[] s = new PropertyValue[handleMap.length];
+ int n = 0;
+ for (int i = 0; i < handleMap.length; ++i) {
+ PropertyState[] state = new PropertyState[1];
+ Object value;
+ try {
+ value = getProperty(handleMap[i], state);
+ } catch (UnknownPropertyException e) {
+ continue;
+ } catch (WrappedTargetException e) {
+ throw new WrappedTargetRuntimeException(e.getCause(),
+ e.getMessage(), object, e.TargetException);
+ }
+ s[n++] = new PropertyValue(handleMap[i], i, value, state[0]);
+ }
+ if (n < handleMap.length) {
+ PropertyValue[] s2 = new PropertyValue[n];
+ System.arraycopy(s, 0, s2, 0, n);
+ s = s2;
+ }
+ return s;
+ }
+
+ /**
+ Implements
+ <code>com.sun.star.beans.XPropertyAccess.setPropertyValues</code>.
+
+ @param props
+ See com.sun.star.beans.XPropertyAccess
+ @throws UnknownPropertyException
+ See com.sun.star.beans.XPropertyAccess
+ @throws PropertyVetoException
+ See com.sun.star.beans.XPropertyAccess
+ @throws WrappedTargetException
+ See com.sun.star.beans.XPropertyAccess
+ */
+ public void setPropertyValues(PropertyValue[] props)
+ throws UnknownPropertyException, PropertyVetoException,
+ com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ for (int i = 0; i < props.length; ++i) {
+ if (props[i].Handle != -1
+ && !props[i].Name.equals(translateHandle(props[i].Handle)))
+ {
+ throw new UnknownPropertyException(
+ ("name " + props[i].Name + " does not match handle "
+ + props[i].Handle),
+ object);
+ }
+ setProperty(
+ props[i].Name, props[i].Value,
+ props[i].State == PropertyState.AMBIGUOUS_VALUE,
+ props[i].State == PropertyState.DEFAULT_VALUE, (short) 0);
+ }
+ }
+
+ /**
+ A class used by clients of {@link PropertySetMixin} when implementing UNO
+ interface type attribute setter functions.
+
+ @see #prepareSet(String, Object, Object, PropertySetMixin.BoundListeners)
+ */
+ public static final class BoundListeners {
+
+ /**
+ Notifies any
+ <code>com.sun.star.beans.XPropertyChangeListener</code>s.
+
+ @see #prepareSet(String, Object, Object,
+ PropertySetMixin.BoundListeners)
+ */
+ public void notifyListeners() {
+ if (specificListeners != null) {
+ for (Iterator<XPropertyChangeListener> i = specificListeners.iterator(); i.hasNext();) {
+ try {
+ i.next().propertyChange(event);
+ } catch (DisposedException e) {}
+ }
+ }
+ if (unspecificListeners != null) {
+ for (Iterator<XPropertyChangeListener> i = unspecificListeners.iterator(); i.hasNext();)
+ {
+ try {
+ i.next().propertyChange(event);
+ } catch (DisposedException e) {}
+ }
+ }
+ }
+
+ private ArrayList<XPropertyChangeListener> specificListeners = null;
+ private ArrayList<XPropertyChangeListener> unspecificListeners = null;
+ private PropertyChangeEvent event = null;
+ }
+
+ private XIdlClass getReflection(String typeName) {
+ return theCoreReflection.get(context).forName(typeName);
+ }
+
+ private void initProperties(
+ XTypeDescription type, HashMap<String,PropertyData> map, ArrayList<String> handleNames, HashSet<String> seen)
+ {
+ XInterfaceTypeDescription2 ifc = UnoRuntime.queryInterface(
+ XInterfaceTypeDescription2.class, resolveTypedefs(type));
+ if (!seen.add(ifc.getName())) {
+ return;
+ }
+ XTypeDescription[] bases = ifc.getBaseTypes();
+ for (int i = 0; i < bases.length; ++i) {
+ initProperties(bases[i], map, handleNames, seen);
+ }
+ XInterfaceMemberTypeDescription[] members = ifc.getMembers();
+ for (int i = 0; i < members.length; ++i) {
+ if (members[i].getTypeClass() == TypeClass.INTERFACE_ATTRIBUTE)
+ {
+ XInterfaceAttributeTypeDescription2 attr =
+ UnoRuntime.queryInterface(
+ XInterfaceAttributeTypeDescription2.class,
+ members[i]);
+ short attrAttribs = 0;
+ if (attr.isBound()) {
+ attrAttribs |= PropertyAttribute.BOUND;
+ }
+ boolean setUnknown = false;
+ if (attr.isReadOnly()) {
+ attrAttribs |= PropertyAttribute.READONLY;
+ setUnknown = true;
+ }
+ XCompoundTypeDescription[] excs = attr.getGetExceptions();
+ boolean getUnknown = false;
+ //XXX Special interpretation of getter/setter exceptions
+ // only works if the specified exceptions are of the exact
+ // type, not of a supertype:
+ for (int j = 0; j < excs.length; ++j) {
+ if (excs[j].getName().equals(
+ "com.sun.star.beans.UnknownPropertyException"))
+ {
+ getUnknown = true;
+ break;
+ }
+ }
+ excs = attr.getSetExceptions();
+ for (int j = 0; j < excs.length; ++j) {
+ if (excs[j].getName().equals(
+ "com.sun.star.beans.UnknownPropertyException"))
+ {
+ setUnknown = true;
+ } else if (excs[j].getName().equals(
+ "com.sun.star.beans."
+ + "PropertyVetoException"))
+ {
+ attrAttribs |= PropertyAttribute.CONSTRAINED;
+ }
+ }
+ if (getUnknown && setUnknown) {
+ attrAttribs |= PropertyAttribute.OPTIONAL;
+ }
+ XTypeDescription t = attr.getType();
+ for (;;) {
+ t = resolveTypedefs(t);
+ short n;
+ if (t.getName().startsWith(
+ "com.sun.star.beans.Ambiguous<"))
+ {
+ n = PropertyAttribute.MAYBEAMBIGUOUS;
+ } else if (t.getName().startsWith(
+ "com.sun.star.beans.Defaulted<"))
+ {
+ n = PropertyAttribute.MAYBEDEFAULT;
+ } else if (t.getName().startsWith(
+ "com.sun.star.beans.Optional<"))
+ {
+ n = PropertyAttribute.MAYBEVOID;
+ } else {
+ break;
+ }
+ attrAttribs |= n;
+ t = UnoRuntime.queryInterface(XStructTypeDescription.class, t).getTypeArguments()[0];
+ }
+ String name = members[i].getMemberName();
+ boolean present = true;
+ if (absentOptional != null) {
+ for (int j = 0; j < absentOptional.length; ++j) {
+ if (name.equals(absentOptional[j])) {
+ present = false;
+ break;
+ }
+ }
+ }
+ if (map.put(
+ name,
+ new PropertyData(
+ new Property(
+ name, handleNames.size(),
+ new Type(t.getName(), t.getTypeClass()),
+ attrAttribs),
+ present))
+ != null)
+ {
+ throw new RuntimeException(
+ "inconsistent UNO type registry");
+ }
+ handleNames.add(name);
+ }
+ }
+ }
+
+ private String translateHandle(int handle) throws UnknownPropertyException {
+ if (handle < 0 || handle >= handleMap.length) {
+ throw new UnknownPropertyException("bad handle " + handle, object);
+ }
+ return handleMap[handle];
+ }
+
+ private void setProperty(
+ String name, Object value, boolean isAmbiguous, boolean isDefaulted,
+ short illegalArgumentPosition)
+ throws UnknownPropertyException, PropertyVetoException,
+ com.sun.star.lang.IllegalArgumentException, WrappedTargetException
+ {
+ PropertyData p = properties.get(name);
+ if (p == null) {
+ throw new UnknownPropertyException(name, object);
+ }
+ if ((isAmbiguous
+ && (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) == 0)
+ || (isDefaulted
+ && ((p.property.Attributes & PropertyAttribute.MAYBEDEFAULT)
+ == 0)))
+ {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ ("flagging as ambiguous/defaulted non-ambiguous/defaulted"
+ + " property " + name),
+ object, illegalArgumentPosition);
+
+ }
+ XIdlField2 f = UnoRuntime.queryInterface(
+ XIdlField2.class, idlClass.getField(name));
+ Object[] o = new Object[] {
+ new Any(type, UnoRuntime.queryInterface(type, object)) };
+ Object v = wrapValue(
+ value,
+ UnoRuntime.queryInterface(
+ XIdlField2.class, idlClass.getField(name)).getType(),
+ (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0,
+ isAmbiguous,
+ (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0,
+ isDefaulted,
+ (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0);
+ try {
+ f.set(o, v);
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ if (e.ArgumentPosition == 1) {
+ throw new com.sun.star.lang.IllegalArgumentException(e,
+ e.getMessage(), object, illegalArgumentPosition);
+ } else {
+ throw new RuntimeException(e);
+ }
+ } catch (com.sun.star.lang.IllegalAccessException e) {
+ //TODO Clarify whether PropertyVetoException is the correct
+ // exception to throw when trying to set a read-only property:
+ throw new PropertyVetoException(e,
+ "cannot set read-only property " + name, object);
+ } catch (WrappedTargetRuntimeException e) {
+ //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
+ // guaranteed to originate directly within XIdlField2.get (and thus
+ // have the expected semantics); it might also be passed through
+ // from lower layers.
+ if (new Type(UnknownPropertyException.class).isSupertypeOf(
+ AnyConverter.getType(e.TargetException))
+ && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0)
+ {
+ throw new UnknownPropertyException(e, name, object);
+ } else if (new Type(PropertyVetoException.class).isSupertypeOf(
+ AnyConverter.getType(e.TargetException))
+ && ((p.property.Attributes
+ & PropertyAttribute.CONSTRAINED)
+ != 0))
+ {
+ throw new PropertyVetoException(e, name, object);
+ } else {
+ throw new WrappedTargetException(e.getCause(),
+ e.getMessage(), object, e.TargetException);
+ }
+ }
+ }
+
+ Object getProperty(String name, PropertyState[] state)
+ throws UnknownPropertyException, WrappedTargetException
+ {
+ PropertyData p = properties.get(name);
+ if (p == null) {
+ throw new UnknownPropertyException(name, object);
+ }
+ XIdlField2 field = UnoRuntime.queryInterface(
+ XIdlField2.class, idlClass.getField(name));
+ Object value;
+ try {
+ value = field.get(
+ new Any(type, UnoRuntime.queryInterface(type, object)));
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (WrappedTargetRuntimeException e) {
+ //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
+ // guaranteed to originate directly within XIdlField2.get (and thus
+ // have the expected semantics); it might also be passed through
+ // from lower layers.
+ if (new Type(UnknownPropertyException.class).isSupertypeOf(
+ AnyConverter.getType(e.TargetException))
+ && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0)
+ {
+ throw new UnknownPropertyException(e, name, object);
+ } else {
+ throw new WrappedTargetException(e.getCause(),
+ e.getMessage(), object, e.TargetException);
+ }
+ }
+ boolean undoAmbiguous
+ = (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0;
+ boolean undoDefaulted
+ = (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0;
+ boolean undoOptional
+ = (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0;
+ boolean isAmbiguous = false;
+ boolean isDefaulted = false;
+ while (undoAmbiguous || undoDefaulted || undoOptional) {
+ String typeName = AnyConverter.getType(value).getTypeName();
+ if (undoAmbiguous
+ && typeName.startsWith("com.sun.star.beans.Ambiguous<"))
+ {
+ XIdlClass ambiguous = getReflection(typeName);
+ try {
+ isAmbiguous = AnyConverter.toBoolean(
+ UnoRuntime.queryInterface(
+ XIdlField2.class,
+ ambiguous.getField("IsAmbiguous")).get(value));
+ value = UnoRuntime.queryInterface(
+ XIdlField2.class,
+ ambiguous.getField("Value")).get(value);
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ }
+ undoAmbiguous = false;
+ } else if (undoDefaulted
+ && typeName.startsWith("com.sun.star.beans.Defaulted<"))
+ {
+ XIdlClass defaulted = getReflection(typeName);
+ try {
+ isDefaulted = AnyConverter.toBoolean(
+ UnoRuntime.queryInterface(
+ XIdlField2.class,
+ defaulted.getField("IsDefaulted")).get(value));
+ value = UnoRuntime.queryInterface(
+ XIdlField2.class,
+ defaulted.getField("Value")).get(value);
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ }
+ undoDefaulted = false;
+ } else if (undoOptional
+ && typeName.startsWith("com.sun.star.beans.Optional<"))
+ {
+ XIdlClass optional = getReflection(typeName);
+ try {
+ boolean present = AnyConverter.toBoolean(
+ UnoRuntime.queryInterface(
+ XIdlField2.class,
+ optional.getField("IsPresent")).get(value));
+ if (!present) {
+ value = Any.VOID;
+ break;
+ }
+ value = UnoRuntime.queryInterface(
+ XIdlField2.class,
+ optional.getField("Value")).get(value);
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ }
+ undoOptional = false;
+ } else {
+ throw new RuntimeException(
+ "unexpected type of attribute " + name);
+ }
+ }
+ if (state != null) {
+ //XXX If isAmbiguous && isDefaulted, arbitrarily choose
+ // AMBIGUOUS_VALUE over DEFAULT_VALUE:
+ state[0] = isAmbiguous
+ ? PropertyState.AMBIGUOUS_VALUE
+ : isDefaulted
+ ? PropertyState.DEFAULT_VALUE : PropertyState.DIRECT_VALUE;
+ }
+ return value;
+ }
+
+ private Object wrapValue(
+ Object value, XIdlClass type, boolean wrapAmbiguous,
+ boolean isAmbiguous, boolean wrapDefaulted, boolean isDefaulted,
+ boolean wrapOptional)
+ {
+ if (wrapAmbiguous
+ && type.getName().startsWith("com.sun.star.beans.Ambiguous<"))
+ {
+ Object[] strct = new Object[1];
+ type.createObject(strct);
+ try {
+ XIdlField2 field = UnoRuntime.queryInterface(
+ XIdlField2.class, type.getField("Value"));
+ field.set(
+ strct,
+ wrapValue(
+ value, field.getType(), false, false, wrapDefaulted,
+ isDefaulted, wrapOptional));
+ UnoRuntime.queryInterface(
+ XIdlField2.class, type.getField("IsAmbiguous")).set(
+ strct, Boolean.valueOf(isAmbiguous));
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (com.sun.star.lang.IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ return strct[0];
+ } else if (wrapDefaulted
+ && type.getName().startsWith(
+ "com.sun.star.beans.Defaulted<"))
+ {
+ Object[] strct = new Object[1];
+ type.createObject(strct);
+ try {
+ XIdlField2 field = UnoRuntime.queryInterface(
+ XIdlField2.class, type.getField("Value"));
+ field.set(
+ strct,
+ wrapValue(
+ value, field.getType(), wrapAmbiguous, isAmbiguous,
+ false, false, wrapOptional));
+ UnoRuntime.queryInterface(
+ XIdlField2.class, type.getField("IsDefaulted")).set(
+ strct, Boolean.valueOf(isDefaulted));
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (com.sun.star.lang.IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ return strct[0];
+ } else if (wrapOptional
+ && type.getName().startsWith("com.sun.star.beans.Optional<"))
+ {
+ Object[] strct = new Object[1];
+ type.createObject(strct);
+ boolean present = !AnyConverter.isVoid(value);
+ try {
+ UnoRuntime.queryInterface(
+ XIdlField2.class, type.getField("IsPresent")).set(
+ strct, Boolean.valueOf(present));
+ if (present) {
+ XIdlField2 field = UnoRuntime.queryInterface(
+ XIdlField2.class, type.getField("Value"));
+ field.set(
+ strct,
+ wrapValue(
+ value, field.getType(), wrapAmbiguous, isAmbiguous,
+ wrapDefaulted, isDefaulted, false));
+ }
+ } catch (com.sun.star.lang.IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (com.sun.star.lang.IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ return strct[0];
+ } else {
+ if (wrapAmbiguous || wrapDefaulted || wrapOptional) {
+ throw new RuntimeException("unexpected type of attribute");
+ }
+ return value;
+ }
+ }
+
+ private static XTypeDescription resolveTypedefs(XTypeDescription type) {
+ while (type.getTypeClass() == TypeClass.TYPEDEF) {
+ type = UnoRuntime.queryInterface(
+ XIndirectTypeDescription.class, type).getReferencedType();
+ }
+ return type;
+ }
+
+ private PropertyData get(Object object, String propertyName)
+ throws UnknownPropertyException
+ {
+ PropertyData p = properties.get(propertyName);
+ if (p == null || !p.present) {
+ throw new UnknownPropertyException(propertyName, object);
+ }
+ return p;
+ }
+
+ private void checkUnknown(String propertyName)
+ throws UnknownPropertyException
+ {
+ if (propertyName.length() != 0) {
+ get(this, propertyName);
+ }
+ }
+
+ private static final class PropertyData {
+ public PropertyData(Property property, boolean present) {
+ this.property = property;
+ this.present = present;
+ }
+
+ public final Property property;
+ public final boolean present;
+ }
+
+ private final class Info extends WeakBase implements XPropertySetInfo
+ {
+ public Info(Map<String,PropertyData> properties) {
+ this.properties = properties;
+ }
+
+ public Property[] getProperties() {
+ ArrayList<Property> al = new ArrayList<Property>(properties.size());
+ for (Iterator<PropertyData> i = properties.values().iterator(); i.hasNext();) {
+ PropertyData p = i.next();
+ if (p.present) {
+ al.add(p.property);
+ }
+ }
+ return al.toArray(new Property[al.size()]);
+ }
+
+ public Property getPropertyByName(String name)
+ throws UnknownPropertyException
+ {
+ return get(this, name).property;
+ }
+
+ public boolean hasPropertyByName(String name) {
+ PropertyData p = properties.get(name);
+ return p != null && p.present;
+ }
+
+ private final Map<String,PropertyData> properties;
+ }
+
+ private final XComponentContext context;
+ private final XInterface object;
+ private final Type type;
+ private final String[] absentOptional;
+ private final XIdlClass idlClass;
+ private final Map<String,PropertyData> properties; // from String to Property
+ private final String[] handleMap;
+
+ private HashMap<String,ArrayList<XPropertyChangeListener>> boundListeners
+ = new HashMap<String,ArrayList<XPropertyChangeListener>>();
+ // from String to Vector of XPropertyChangeListener
+ private HashMap<String,ArrayList<XVetoableChangeListener>> vetoListeners
+ = new HashMap<String,ArrayList<XVetoableChangeListener>>();
+ // from String to Vector of XVetoableChangeListener
+ private boolean disposed = false;
+}
diff --git a/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java b/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java
new file mode 100644
index 000000000..8bb4d2643
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java
@@ -0,0 +1,401 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+/**
+ * Object representation and parsing of Uno Urls,
+ * which allow to locate a named Uno object in a
+ * different process. A Uno Url consists of the
+ * specification of a connection, protocol and
+ * rootOid delimited with a ';'.
+ * The syntax of a Uno Url is
+ *
+ * <code>
+ * [uno:]connection-type,parameters;protocol-name,parameters;objectname";
+ * </code>
+ *
+ * An example Uno Url will look like this:
+ *
+ * <code>
+ * socket,host=localhost,port=2002;urp;StarOffice.ServiceManager
+ * </code>
+ *
+ * For more information about Uno Url please consult
+ * <a href="http://udk.openoffice.org/common/man/spec/uno-url.html">
+ * http://udk.openoffice.org/common/man/spec/uno-url.html</a>
+ *
+ * Usage:
+ *
+ * <code>
+ * UnoUrl url = UnoUrl.parseUnoUrl("socket,host=localhost,port=2002;urp;StarOffice.ServiceManager");
+ * </code>
+ */
+public class UnoUrl {
+
+ private static final String FORMAT_ERROR =
+ "syntax: [uno:]connection-type,parameters;protocol-name,parameters;objectname";
+
+ private static final String VALUE_CHAR_SET = "!$&'()*+-./:?@_~";
+ private static final String OID_CHAR_SET = VALUE_CHAR_SET + ",=";
+
+ private final UnoUrlPart connection;
+ private final UnoUrlPart protocol;
+ private final String rootOid;
+
+ private static class UnoUrlPart {
+
+ private final String partTypeName;
+ private final HashMap<String,String> partParameters;
+ private final String uninterpretedParameterString;
+
+ public UnoUrlPart(
+ String uninterpretedParameterString,
+ String partTypeName,
+ HashMap<String,String> partParameters) {
+ this.uninterpretedParameterString = uninterpretedParameterString;
+ this.partTypeName = partTypeName;
+ this.partParameters = partParameters;
+ }
+
+ public String getPartTypeName() {
+ return partTypeName;
+ }
+
+ public HashMap<String,String> getPartParameters() {
+ return partParameters;
+ }
+
+ public String getUninterpretedParameterString() {
+ return uninterpretedParameterString;
+ }
+
+ public String getUninterpretedString() {
+ StringBuffer buf = new StringBuffer(partTypeName);
+ if (uninterpretedParameterString.length() > 0) {
+ buf.append(',');
+ buf.append(uninterpretedParameterString);
+ }
+ return buf.toString();
+ }
+ }
+
+ private UnoUrl(
+ UnoUrlPart connectionPart,
+ UnoUrlPart protocolPart,
+ String rootOid) {
+ this.connection = connectionPart;
+ this.protocol = protocolPart;
+ this.rootOid = rootOid;
+ }
+
+ /**
+ * Returns the name of the connection of this
+ * Uno Url. Encoded characters are not allowed.
+ *
+ * @return The connection name as string.
+ */
+ public String getConnection() {
+ return connection.getPartTypeName();
+ }
+
+ /**
+ * Returns the name of the protocol of this
+ * Uno Url. Encoded characters are not allowed.
+ *
+ * @return The protocol name as string.
+ */
+ public String getProtocol() {
+ return protocol.getPartTypeName();
+ }
+
+ /**
+ * Return the object name. Encoded character are
+ * not allowed.
+ *
+ * @return The object name as String.
+ */
+ public String getRootOid() {
+ return rootOid;
+ }
+
+ /**
+ * Returns the protocol parameters as
+ * a Hashmap with key/value pairs. Encoded
+ * characters like '%41' are decoded.
+ *
+ * @return a HashMap with key/value pairs for protocol parameters.
+ */
+ public HashMap<String,String> getProtocolParameters() {
+ return protocol.getPartParameters();
+ }
+
+ /**
+ * Returns the connection parameters as
+ * a Hashmap with key/value pairs. Encoded
+ * characters like '%41' are decoded.
+ *
+ * @return a HashMap with key/value pairs for connection parameters.
+ */
+ public HashMap<String,String> getConnectionParameters() {
+ return connection.getPartParameters();
+ }
+
+ /**
+ * Returns the raw specification of the protocol
+ * parameters. Encoded characters like '%41' are
+ * not decoded.
+ *
+ * @return The uninterpreted protocol parameters as string.
+ */
+ public String getProtocolParametersAsString() {
+ return protocol.getUninterpretedParameterString();
+ }
+
+ /**
+ * Returns the raw specification of the connection
+ * parameters. Encoded characters like '%41' are
+ * not decoded.
+ *
+ * @return The uninterpreted connection parameters as string.
+ */
+ public String getConnectionParametersAsString() {
+ return connection.getUninterpretedParameterString();
+ }
+
+ /**
+ * Returns the raw specification of the protocol
+ * name and parameters. Encoded characters like '%41' are
+ * not decoded.
+ *
+ * @return The uninterpreted protocol name and parameters as string.
+ */
+ public String getProtocolAndParametersAsString() {
+ return protocol.getUninterpretedString();
+ }
+
+ /**
+ * Returns the raw specification of the connection
+ * name and parameters. Encoded characters like '%41' are
+ * not decoded.
+ *
+ * @return The uninterpreted connection name and parameters as string.
+ */
+ public String getConnectionAndParametersAsString() {
+ return connection.getUninterpretedString();
+ }
+
+ private static String decodeUTF8(String s)
+ throws com.sun.star.lang.IllegalArgumentException {
+
+ if (!s.contains("%")) {
+ return s;
+ }
+ try {
+ int length = s.length();
+ ByteBuffer bb = ByteBuffer.allocate(length);
+ for (int i = 0; i < length; i++) {
+ int ch = s.charAt(i);
+
+ if (ch == '%') {
+ if (i+3 > length)
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "Incomplete trailing escape (%) pattern");
+ try {
+ ch = Integer.parseInt(s.substring(i+1,i+3),16);
+ } catch (NumberFormatException e) {
+ throw new com.sun.star.lang.IllegalArgumentException(e);
+ }
+ if (ch < 0)
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "Illegal hex characters in escape (%) pattern - negative value");
+ i+=2;
+ }
+
+ bb.put((byte) (ch & 0xFF));
+ }
+
+ byte[] bytes = new byte[bb.position()];
+ System.arraycopy(bb.array(), 0, bytes, 0, bytes.length);
+ return new String(bytes, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new com.sun.star.lang.IllegalArgumentException(e,
+ "Couldn't convert parameter string to UTF-8 string");
+ }
+ }
+
+ private static HashMap<String,String> buildParamHashMap(String paramString)
+ throws com.sun.star.lang.IllegalArgumentException {
+ HashMap<String,String> params = new HashMap<String,String>();
+
+ int pos = 0;
+
+ while (true) {
+ char c = ',';
+
+ StringBuffer sb = new StringBuffer();
+ while ((pos < paramString.length())
+ && ((c = paramString.charAt(pos++)) != '=')) {
+ sb.append(c);
+ }
+ String aKey = sb.toString();
+
+ sb = new StringBuffer();
+ while ((pos < paramString.length())
+ && ((c = paramString.charAt(pos++)) != ',')
+ && c != ';') {
+ sb.append(c);
+ }
+ String aValue = sb.toString();
+
+ if ((aKey.length() > 0) && (aValue.length() > 0)) {
+
+ if (!isAlphaNumeric(aKey)) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "The parameter key '"
+ + aKey
+ + "' may only consist of alpha numeric ASCII characters.");
+ }
+
+ if (!isValidString(aValue, VALUE_CHAR_SET + "%")) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "The parameter value for key '" + aKey + "' contains illegal characters.");
+ }
+
+ params.put(aKey, decodeUTF8(aValue));
+ }
+
+ if ((pos >= paramString.length()) || (c != ','))
+ break;
+
+ }
+
+ return params;
+ }
+
+ private static UnoUrlPart parseUnoUrlPart(String thePart)
+ throws com.sun.star.lang.IllegalArgumentException {
+ String partName;
+ String theParamPart;
+ int index = thePart.indexOf(',');
+ if (index != -1) {
+ partName = thePart.substring(0, index).trim();
+ theParamPart = thePart.substring(index + 1).trim();
+ } else {
+ partName = thePart;
+ theParamPart = "";
+ }
+
+ if (!isAlphaNumeric(partName)) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "The part name '"
+ + partName
+ + "' may only consist of alpha numeric ASCII characters.");
+ }
+
+ HashMap<String,String> params = buildParamHashMap(theParamPart);
+
+ return new UnoUrlPart(theParamPart, partName, params);
+ }
+
+ private static boolean isAlphaNumeric(String s) {
+ return isValidString(s, null);
+ }
+
+ private static boolean isValidString(String identifier, String validCharSet) {
+
+ int len = identifier.length();
+
+ for (int i = 0; i < len; i++) {
+
+ int ch = identifier.charAt(i);
+
+ boolean isValidChar =
+ ('A' <= ch && ch <= 'Z')
+ || ('a' <= ch && ch <= 'z')
+ || ('0' <= ch && ch <= '9');
+
+ if (!isValidChar && (validCharSet != null)) {
+ isValidChar = (validCharSet.indexOf(ch) != -1);
+ }
+
+ if (!isValidChar)
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Parses the given Uno Url and returns
+ * an in memory object representation.
+ *
+ * @param unoUrl The given uno URl as string.
+ * @return Object representation of class UnoUrl.
+ * @throws IllegalArgumentException if Url cannot be parsed.
+ */
+ public static UnoUrl parseUnoUrl(String unoUrl)
+ throws com.sun.star.lang.IllegalArgumentException {
+
+ String url = unoUrl;
+
+ int index = url.indexOf(':');
+ if (index != -1) {
+ String unoStr = url.substring(0, index).trim();
+ if (!"uno".equals(unoStr)) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "Uno Urls must start with 'uno:'. " + FORMAT_ERROR);
+ }
+ }
+
+ url = url.substring(index + 1).trim();
+
+ index = url.indexOf(';');
+ if (index == -1) {
+ throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR);
+ }
+
+ String connection = url.substring(0, index).trim();
+ url = url.substring(index + 1).trim();
+
+ UnoUrlPart connectionPart = parseUnoUrlPart(connection);
+
+ index = url.indexOf(';');
+ if (index == -1) {
+ throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR);
+ }
+
+ String protocol = url.substring(0, index).trim();
+ url = url.substring(index + 1).trim();
+
+ UnoUrlPart protocolPart = parseUnoUrlPart(protocol);
+
+ String rootOid = url.trim();
+ if (!isValidString(rootOid, OID_CHAR_SET)) {
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "Root OID '"+ rootOid + "' contains illegal characters.");
+ }
+
+ return new UnoUrl(connectionPart, protocolPart, rootOid);
+
+ }
+
+}
diff --git a/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java b/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java
new file mode 100644
index 000000000..67e01ac32
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+import java.lang.ref.WeakReference;
+import com.sun.star.uno.XAdapter;
+import com.sun.star.uno.XReference;
+import java.util.List;
+import java.util.Collections;
+import java.util.LinkedList;
+
+/** An XAdapter implementation that holds a weak reference (java.lang.ref.WeakReference)
+ * to an object. Clients can register listener (com.sun.star.lang.XReference) which
+ * are notified when the object (the one which is kept weak) is being finalized. That
+ * is, that object is being destroyed because there are not any hard references
+ * to it.
+ */
+public class WeakAdapter implements XAdapter
+{
+ // references the XWeak implementation
+ private final WeakReference<Object> m_weakRef;
+ // contains XReference objects registered by addReference
+ private final List<XReference> m_xreferenceList;
+
+ /**
+ *@param component the object that is to be held weak
+ */
+ public WeakAdapter(Object component)
+ {
+ m_weakRef= new WeakReference<Object>(component);
+ m_xreferenceList= Collections.synchronizedList( new LinkedList<XReference>());
+ }
+
+ /** Called by the XWeak implementation (WeakBase) when it is being finalized.
+ * It is only being called once.
+ * The registered XReference listeners are notified. On notification they are
+ * to unregister themselves. The notification is thread-safe. However, it is possible
+ * to add a listener during the notification process, which will never receive a
+ * notification. To prevent this, one would have to synchronize this method with
+ * the addReference method. But this can result in deadlocks in a multi-threaded
+ * environment.
+ */
+ void referentDying()
+ {
+ //synchronized call
+ XReference[] references= m_xreferenceList.toArray(new XReference[m_xreferenceList.size()]);
+ for (int i= references.length; i > 0; i--)
+ {
+ references[i-1].dispose();
+ }
+ }
+
+ /** Method of com.sun.star.uno.XAdapter. It is called to obtain a hard reference
+ * to the object which is kept weak by this instance.
+ * @return hard reference to the object
+ */
+ public Object queryAdapted()
+ {
+ return m_weakRef.get();
+ }
+
+ /** Method of com.sun.star.uno.XAdapter. Called by clients to register listener which
+ * are notified when the weak object is dying.
+ *@param xReference a listener
+ */
+ public void removeReference(XReference xReference)
+ {
+ m_xreferenceList.remove(xReference);
+ }
+
+ /** Method of com.sun.star.uno.XAdapter. Called by clients to unregister listeners.
+ *@param xReference listener
+ */
+ public void addReference(XReference xReference)
+ {
+ m_xreferenceList.add(xReference);
+ }
+}
+
diff --git a/ridljar/com/sun/star/lib/uno/helper/WeakBase.java b/ridljar/com/sun/star/lib/uno/helper/WeakBase.java
new file mode 100644
index 000000000..ac175d3a6
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/helper/WeakBase.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.helper;
+import com.sun.star.uno.XWeak;
+import com.sun.star.uno.XAdapter;
+import com.sun.star.lang.XTypeProvider;
+import com.sun.star.uno.Type;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+
+/** This class can be used as the base class for UNO components. It implements the capability
+ * to be kept weak (com.sun.star.uno.XWeak) and it implements com.sun.star.lang.XTypeProvider
+ * which is necessary for using the component with StarBasic.
+ */
+public class WeakBase implements XWeak, XTypeProvider
+{
+ // Contains all WeakAdapter which have been created in this class
+ // They have to be notified when this object dies
+ private WeakAdapter m_adapter;
+
+ protected static Map<Class<?>,Type[]> _mapTypes = new HashMap<Class<?>,Type[]>();
+
+ /** Method of XWeak. The returned XAdapter implementation can be used to keep
+ * a weak reference to this object.
+ * @return a com.sun.star.uno.XAdapter implementation.
+ */
+ synchronized public XAdapter queryAdapter()
+ {
+ if (m_adapter == null)
+ m_adapter= new WeakAdapter(this);
+ return m_adapter;
+ }
+
+ /** Override of Object.finalize. When there are no references to this object anymore
+ * then the garbage collector calls this method. Thereby causing the adapter object
+ * to be notified. The adapter, in turn, notifies all listeners (com.sun.star.uno.XReference)
+ */
+ @Override
+ protected void finalize() throws java.lang.Throwable
+ {
+ if (m_adapter != null)
+ m_adapter.referentDying();
+ super.finalize();
+ }
+
+ /** Method of XTypeProvider. It returns an array of Type objects which represent
+ * all implemented UNO interfaces of this object.
+ * @return Type objects of all implemented interfaces.
+ */
+ public Type[] getTypes()
+ {
+ Type[] arTypes= _mapTypes.get( getClass());
+ if (arTypes == null)
+ {
+ ArrayList<Type> vec= new ArrayList<Type>();
+ Class currentClass= getClass();
+ do
+ {
+ Class interfaces[]= currentClass.getInterfaces();
+ for(int i = 0; i < interfaces.length; ++ i)
+ {
+ // Test if it is a UNO interface
+ if (com.sun.star.uno.XInterface.class.isAssignableFrom(interfaces[i]))
+ vec.add(new Type(interfaces[i]));
+ }
+ // get the superclass the currentClass inherits from
+ currentClass= currentClass.getSuperclass();
+ } while (currentClass != null);
+
+ Type types[]= vec.toArray(new Type[vec.size()]);
+ _mapTypes.put(getClass(), types);
+ arTypes= types;
+ }
+ return arTypes;
+ }
+
+ /** Obsolete method of XTypeProvider.
+ */
+ public byte[] getImplementationId()
+ {
+ return new byte[0];
+ }
+}
diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java b/ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java
new file mode 100644
index 000000000..26c2d449d
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java
@@ -0,0 +1,114 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.protocols.urp;
+
+import java.util.HashMap;
+
+/**
+ * An LRU cache for arbitrary objects.
+ *
+ * <p>This class is not synchronized, as any necessary synchronization will already
+ * take place in the client.</p>
+ */
+final class Cache {
+ /**
+ * Create a cache.
+ *
+ * @param size the maximum cache size, must be between 0, inclusive, and
+ * NOT_CACHED, exclusive.
+ */
+ public Cache(int size) {
+ maxSize = size;
+ }
+
+ public int add(boolean[] found, Object content) {
+ Entry e = map.get(content);
+ found[0] = e != null;
+ if (e == null) {
+ if (map.size() < maxSize) {
+ // There is still room for a new entry at the front:
+ e = new Entry(content, map.size(), null, first);
+ if (first == null) {
+ last = e;
+ } else {
+ first.prev = e;
+ }
+ first = e;
+ } else if (last != null) {
+ // Take last entry out and recycle as new front:
+ map.remove(last.content);
+ e = last;
+ e.content = content;
+ if (first != last) {
+ // Reached only if maxSize > 1:
+ last = last.prev;
+ last.next = null;
+ e.prev = null;
+ e.next = first;
+ first.prev = e;
+ first = e;
+ }
+ } else {
+ // Reached iff maxSize == 0:
+ return NOT_CACHED;
+ }
+ map.put(content, e);
+ } else if (e != first) {
+ // Move to front (reached only if maxSize > 1):
+ e.prev.next = e.next;
+ if (e.next == null) {
+ last = e.prev;
+ } else {
+ e.next.prev = e.prev;
+ }
+ e.prev = null;
+ e.next = first;
+ first.prev = e;
+ first = e;
+ }
+ return e.index;
+ }
+
+ public static final int NOT_CACHED = 0xFFFF;
+
+ private static final class Entry {
+ public Entry(Object content, int index, Entry prev, Entry next) {
+ this.content = content;
+ this.index = index;
+ this.prev = prev;
+ this.next = next;
+ }
+
+ public Object content;
+ public int index;
+ public Entry prev;
+ public Entry next;
+ }
+
+ // first/last form a list of 0 to maxSize entries, most recently used first;
+ // map contains the same entries; each entry has a unique index in the range
+ // 0 to maxSize - 1
+ private final int maxSize;
+ private final HashMap<Object, Entry> map = new HashMap<Object, Entry>(); // from Object to Entry
+ private Entry first = null;
+ private Entry last = null;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java b/ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java
new file mode 100644
index 000000000..106a8736c
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java
@@ -0,0 +1,355 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.protocols.urp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.typedesc.FieldDescription;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.Enum;
+import com.sun.star.uno.IBridge;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.XInterface;
+
+final class Marshal {
+ public Marshal(IBridge bridge, short cacheSize) {
+ this.bridge = bridge;
+ objectIdCache = new Cache(cacheSize);
+ threadIdCache = new Cache(cacheSize);
+ typeCache = new Cache(cacheSize);
+ }
+
+ public void write8Bit(int value) {
+ try {
+ output.writeByte(value);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void write16Bit(int value) {
+ try {
+ output.writeShort(value);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void writeObjectId(String objectId) {
+ try {
+ if (objectId == null) {
+ writeStringValue(null);
+ write16Bit(0xFFFF);
+ } else {
+ boolean[] found = new boolean[1];
+ int index = objectIdCache.add(found, objectId);
+ writeStringValue(found[0] ? null : objectId);
+ write16Bit(index);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void writeInterface(XInterface object, Type type) {
+ writeObjectId((String) bridge.mapInterfaceTo(object, type));
+ }
+
+ public void writeThreadId(ThreadId threadId) {
+ try {
+ byte[] data = threadId.getBytes();
+ boolean[] found = new boolean[1];
+ int index = threadIdCache.add(found, data);
+ if (found[0]) {
+ writeCompressedNumber(0);
+ } else {
+ writeCompressedNumber(data.length);
+ writeBytes(data);
+ }
+ write16Bit(index);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void writeType(TypeDescription type) {
+ try {
+ TypeClass typeClass = type.getTypeClass();
+ if (TypeDescription.isTypeClassSimple(typeClass)) {
+ write8Bit(typeClass.getValue());
+ } else {
+ boolean[] found = new boolean[1];
+ int index = typeCache.add(found, type.getTypeName());
+ write8Bit(typeClass.getValue() | (found[0] ? 0 : 0x80));
+ write16Bit(index);
+ if (!found[0]) {
+ writeStringValue(type.getTypeName());
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void writeValue(TypeDescription type, Object value) {
+ try {
+ switch(type.getTypeClass().getValue()) {
+ case TypeClass.VOID_value:
+ break;
+
+ case TypeClass.BOOLEAN_value:
+ writeBooleanValue((Boolean) value);
+ break;
+
+ case TypeClass.BYTE_value:
+ writeByteValue((Byte) value);
+ break;
+
+ case TypeClass.SHORT_value:
+ case TypeClass.UNSIGNED_SHORT_value:
+ writeShortValue((Short) value);
+ break;
+
+ case TypeClass.LONG_value:
+ case TypeClass.UNSIGNED_LONG_value:
+ writeLongValue((Integer) value);
+ break;
+
+ case TypeClass.HYPER_value:
+ case TypeClass.UNSIGNED_HYPER_value:
+ writeHyperValue((Long) value);
+ break;
+
+ case TypeClass.FLOAT_value:
+ writeFloatValue((Float) value);
+ break;
+
+ case TypeClass.DOUBLE_value:
+ writeDoubleValue((Double) value);
+ break;
+
+ case TypeClass.CHAR_value:
+ writeCharValue((Character) value);
+ break;
+
+ case TypeClass.STRING_value:
+ writeStringValue((String) value);
+ break;
+
+ case TypeClass.TYPE_value:
+ writeTypeValue((Type) value);
+ break;
+
+ case TypeClass.ANY_value:
+ writeAnyValue(value);
+ break;
+
+ case TypeClass.SEQUENCE_value:
+ writeSequenceValue(type, value);
+ break;
+
+ case TypeClass.ENUM_value:
+ writeEnumValue(type, (Enum) value);
+ break;
+
+ case TypeClass.STRUCT_value:
+ writeStructValue(type, value);
+ break;
+
+ case TypeClass.EXCEPTION_value:
+ writeExceptionValue(type, (Exception) value);
+ break;
+
+ case TypeClass.INTERFACE_value:
+ writeInterfaceValue(type, (XInterface) value);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Bad type descriptor " + type);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public byte[] reset() {
+ byte[] data = buffer.toByteArray();
+ buffer.reset();
+ return data;
+ }
+
+ private void writeBooleanValue(Boolean value) throws IOException {
+ output.writeBoolean(value != null && value.booleanValue());
+ }
+
+ private void writeByteValue(Byte value) {
+ write8Bit(value == null ? 0 : value.byteValue());
+ }
+
+ private void writeShortValue(Short value) {
+ write16Bit(value == null ? 0 : value.shortValue());
+ }
+
+ private void writeLongValue(Integer value) throws IOException {
+ write32Bit(value == null ? 0 : value.intValue());
+ }
+
+ private void writeHyperValue(Long value) throws IOException {
+ output.writeLong(value == null ? 0 : value.longValue());
+ }
+
+ private void writeFloatValue(Float value) throws IOException {
+ output.writeFloat(value == null ? 0 : value.floatValue());
+ }
+
+ private void writeDoubleValue(Double value) throws IOException {
+ output.writeDouble(value == null ? 0 : value.doubleValue());
+ }
+
+ private void writeCharValue(Character value) throws IOException {
+ output.writeChar(value == null ? 0 : value.charValue());
+ }
+
+ private void writeStringValue(String value) throws IOException {
+ if (value == null) {
+ writeCompressedNumber(0);
+ } else {
+ byte[] data = value.getBytes("UTF8");
+ writeCompressedNumber(data.length);
+ writeBytes(data);
+ }
+ }
+
+ private void writeTypeValue(Type value) throws ClassNotFoundException {
+ writeType(
+ TypeDescription.getTypeDescription(
+ value == null ? Type.VOID : value));
+ }
+
+ private void writeAnyValue(Object value) throws ClassNotFoundException {
+ TypeDescription type;
+ if (value == null || value instanceof XInterface) {
+ type = TypeDescription.getTypeDescription(XInterface.class);
+ } else if (value instanceof Any) {
+ Any any = (Any) value;
+ type = TypeDescription.getTypeDescription(any.getType());
+ value = any.getObject();
+ } else if (value.getClass() == Object.class) {
+ // Avoid StackOverflowError:
+ throw new IllegalArgumentException(
+ "Object instance does not represent UNO value");
+ } else {
+ type = TypeDescription.getTypeDescription(value.getClass());
+ }
+ writeType(type);
+ writeValue(type, value);
+ }
+
+ private void writeSequenceValue(TypeDescription type, Object value) throws IOException {
+ if (value == null) {
+ writeCompressedNumber(0);
+ } else {
+ TypeDescription ctype = type.getComponentType();
+ if (ctype.getTypeClass() == TypeClass.BYTE) {
+ byte[] data = (byte[]) value;
+ writeCompressedNumber(data.length);
+ writeBytes(data);
+ } else {
+ int len = Array.getLength(value);
+ writeCompressedNumber(len);
+ for (int i = 0; i < len; ++i) {
+ writeValue(ctype, Array.get(value, i));
+ }
+ }
+ }
+ }
+
+ private void writeEnumValue(TypeDescription type, Enum value) throws IllegalAccessException, IOException, InvocationTargetException, NoSuchMethodException {
+ int n;
+ if (value == null) {
+ n = ((Enum)
+ (type.getZClass().getMethod("getDefault", (Class[]) null).
+ invoke(null, (Object[]) null))).
+ getValue();
+ } else {
+ n = value.getValue();
+ }
+ write32Bit(n);
+ }
+
+ private void writeStructValue(TypeDescription type, Object value) throws IllegalAccessException {
+ FieldDescription[] fields = type.getFieldDescriptions();
+ for (int i = 0; i < fields.length; ++i) {
+ writeValue(
+ fields[i].getTypeDescription(),
+ value == null ? null : fields[i].getField().get(value));
+ }
+ }
+
+ private void writeExceptionValue(TypeDescription type, Exception value) throws IllegalAccessException, IOException {
+ writeStringValue(value == null ? null : value.getMessage());
+ writeStructValue(type, value);
+ }
+
+ private void writeInterfaceValue(TypeDescription type, XInterface value) {
+ writeInterface(value, new Type(type));
+ }
+
+ private void write32Bit(int value) throws IOException {
+ output.writeInt(value);
+ }
+
+ private void writeCompressedNumber(int number) throws IOException {
+ if (number >= 0 && number < 0xFF) {
+ write8Bit(number);
+ } else {
+ write8Bit(0xFF);
+ write32Bit(number);
+ }
+ }
+
+ private void writeBytes(byte[] data) throws IOException {
+ output.write(data);
+ }
+
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ private final DataOutput output = new DataOutputStream(buffer);
+ private final IBridge bridge;
+ private final Cache objectIdCache;
+ private final Cache threadIdCache;
+ private final Cache typeCache;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java b/ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java
new file mode 100644
index 000000000..928cdcd63
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java
@@ -0,0 +1,65 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.protocols.urp;
+
+import java.util.HashMap;
+import java.util.Stack;
+
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+
+final class PendingRequests {
+
+ public synchronized void push(ThreadId tid, Item item) {
+ Stack<Item> s = map.get(tid);
+ if (s == null) {
+ s = new Stack<Item>();
+ map.put(tid, s);
+ }
+ s.push(item);
+ }
+
+ public synchronized Item pop(ThreadId tid) {
+ Stack<Item> s = map.get(tid);
+ Item i = s.pop();
+ if (s.empty()) {
+ map.remove(tid);
+ }
+ return i;
+ }
+
+ public static final class Item {
+ public Item(
+ boolean internal, MethodDescription function, Object[] arguments)
+ {
+ this.internal = internal;
+ this.function = function;
+ this.arguments = arguments;
+ }
+
+ public final boolean internal;
+ public final MethodDescription function;
+ public final Object[] arguments;
+ }
+
+ private final HashMap<ThreadId, Stack<Item>> map = new HashMap<ThreadId, Stack<Item>>(); // from ThreadId to Stack of Item
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java b/ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java
new file mode 100644
index 000000000..c16d19291
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java
@@ -0,0 +1,476 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.protocols.urp;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.Enum;
+import com.sun.star.uno.IBridge;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.XInterface;
+import com.sun.star.lib.uno.typedesc.FieldDescription;
+
+final class Unmarshal {
+ public Unmarshal(IBridge bridge, int cacheSize) {
+ this.bridge = bridge;
+ objectIdCache = new String[cacheSize];
+ threadIdCache = new ThreadId[cacheSize];
+ typeCache = new TypeDescription[cacheSize];
+ reset(new byte[0]);
+ }
+
+ public int read8Bit() {
+ try {
+ return input.readUnsignedByte();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int read16Bit() {
+ try {
+ return input.readUnsignedShort();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String readObjectId() {
+ try {
+ String id = readStringValue();
+ int index = read16Bit();
+ if (index == 0xFFFF) {
+ if (id.length() == 0) {
+ id = null;
+ }
+ } else {
+ if (id.length() == 0) {
+ id = objectIdCache[index];
+ } else {
+ objectIdCache[index] = id;
+ }
+ }
+ return id;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Object readInterface(Type type) {
+ String id = readObjectId();
+ return id == null ? null : bridge.mapInterfaceFrom(id, type);
+ }
+
+ public ThreadId readThreadId() {
+ try {
+ int len = readCompressedNumber();
+ byte[] data ;
+ ThreadId id = null;
+ if (len != 0) {
+ data = new byte[len];
+ readBytes(data);
+ id = new ThreadId(data);
+ }
+ int index = read16Bit();
+ if (index != 0xFFFF) {
+ if (len == 0) {
+ id = threadIdCache[index];
+ } else {
+ threadIdCache[index] = id;
+ }
+ }
+ return id;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public TypeDescription readType() {
+ int b = read8Bit();
+ TypeClass typeClass = TypeClass.fromInt(b & 0x7F);
+ if (typeClass == null) {
+ throw new RuntimeException(
+ "Reading TYPE with bad type class " + (b & 0x7F));
+ }
+ if (TypeDescription.isTypeClassSimple(typeClass)) {
+ if ((b & 0x80) != 0) {
+ throw new RuntimeException(
+ "Reading TYPE with bad type class/cache flag " + b);
+ }
+ return TypeDescription.getTypeDescription(typeClass);
+ } else {
+ int index = read16Bit();
+ TypeDescription type;
+ if ((b & 0x80) == 0) {
+ if (index >= typeCache.length) {
+ throw new RuntimeException(
+ "Reading TYPE with bad cache index " + index);
+ }
+ type = typeCache[index];
+ if (type == null) {
+ throw new RuntimeException(
+ "Reading TYPE with empty cache index " + index);
+ }
+ } else {
+ try {
+ type = TypeDescription.getTypeDescription(
+ readStringValue());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ if (index != 0xFFFF) {
+ if (index >= typeCache.length) {
+ throw new RuntimeException(
+ "Reading TYPE with bad cache index " + index);
+ }
+ typeCache[index] = type;
+ }
+ }
+ return type;
+ }
+ }
+
+ public Object readValue(TypeDescription type) {
+ try {
+ switch (type.getTypeClass().getValue()) {
+ case TypeClass.VOID_value:
+ return null;
+
+ case TypeClass.BOOLEAN_value:
+ return readBooleanValue();
+
+ case TypeClass.BYTE_value:
+ return readByteValue();
+
+ case TypeClass.SHORT_value:
+ case TypeClass.UNSIGNED_SHORT_value:
+ return readShortValue();
+
+ case TypeClass.LONG_value:
+ case TypeClass.UNSIGNED_LONG_value:
+ return readLongValue();
+
+ case TypeClass.HYPER_value:
+ case TypeClass.UNSIGNED_HYPER_value:
+ return readHyperValue();
+
+ case TypeClass.FLOAT_value:
+ return readFloatValue();
+
+ case TypeClass.DOUBLE_value:
+ return readDoubleValue();
+
+ case TypeClass.CHAR_value:
+ return readCharValue();
+
+ case TypeClass.STRING_value:
+ return readStringValue();
+
+ case TypeClass.TYPE_value:
+ return readTypeValue();
+
+ case TypeClass.ANY_value:
+ return readAnyValue();
+
+ case TypeClass.SEQUENCE_value:
+ return readSequenceValue(type);
+
+ case TypeClass.ENUM_value:
+ return readEnumValue(type);
+
+ case TypeClass.STRUCT_value:
+ return readStructValue(type);
+
+ case TypeClass.EXCEPTION_value:
+ return readExceptionValue(type);
+
+ case TypeClass.INTERFACE_value:
+ return readInterfaceValue(type);
+
+ default:
+ throw new IllegalArgumentException("Bad type descriptor " + type);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean hasMore() {
+ try {
+ return input.available() > 0;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void reset(byte[] data) {
+ input = new DataInputStream(new ByteArrayInputStream(data));
+ }
+
+ private Boolean readBooleanValue() throws IOException {
+ return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ private Byte readByteValue() throws IOException {
+ return Byte.valueOf(input.readByte());
+ }
+
+ private Short readShortValue() throws IOException {
+ return Short.valueOf(input.readShort());
+ }
+
+ private Integer readLongValue() throws IOException {
+ return Integer.valueOf(input.readInt());
+ }
+
+ private Long readHyperValue() throws IOException {
+ return Long.valueOf(input.readLong());
+ }
+
+ private Float readFloatValue() throws IOException {
+ return new Float(input.readFloat());
+ }
+
+ private Double readDoubleValue() throws IOException {
+ return new Double(input.readDouble());
+ }
+
+ private Character readCharValue() throws IOException {
+ return new Character(input.readChar());
+ }
+
+ private String readStringValue() throws IOException {
+ int len = readCompressedNumber();
+ byte[] data = new byte[len];
+ readBytes(data);
+ try {
+ return new String(data, "UTF8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Type readTypeValue() {
+ return new Type(readType());
+ }
+
+ private Object readAnyValue() throws IOException {
+ TypeDescription type = readType();
+ switch (type.getTypeClass().getValue()) {
+ case TypeClass.VOID_value:
+ return Any.VOID;
+
+ case TypeClass.BOOLEAN_value:
+ return readBooleanValue();
+
+ case TypeClass.BYTE_value:
+ return readByteValue();
+
+ case TypeClass.SHORT_value:
+ return readShortValue();
+
+ case TypeClass.UNSIGNED_SHORT_value:
+ return new Any(Type.UNSIGNED_SHORT, readShortValue());
+
+ case TypeClass.LONG_value:
+ return readLongValue();
+
+ case TypeClass.UNSIGNED_LONG_value:
+ return new Any(Type.UNSIGNED_LONG, readLongValue());
+
+ case TypeClass.HYPER_value:
+ return readHyperValue();
+
+ case TypeClass.UNSIGNED_HYPER_value:
+ return new Any(Type.UNSIGNED_HYPER, readHyperValue());
+
+ case TypeClass.FLOAT_value:
+ return readFloatValue();
+
+ case TypeClass.DOUBLE_value:
+ return readDoubleValue();
+
+ case TypeClass.CHAR_value:
+ return readCharValue();
+
+ case TypeClass.STRING_value:
+ return readStringValue();
+
+ case TypeClass.TYPE_value:
+ return readTypeValue();
+
+ case TypeClass.SEQUENCE_value:
+ {
+ Object value = readSequenceValue(type);
+ TypeDescription ctype = type.getComponentType();
+ while (ctype.getTypeClass() == TypeClass.SEQUENCE) {
+ ctype = ctype.getComponentType();
+ }
+ switch (ctype.getTypeClass().getValue()) {
+ case TypeClass.UNSIGNED_SHORT_value:
+ case TypeClass.UNSIGNED_LONG_value:
+ case TypeClass.UNSIGNED_HYPER_value:
+ return new Any(new Type(type), value);
+
+ case TypeClass.STRUCT_value:
+ if (ctype.hasTypeArguments()) {
+ return new Any(new Type(type), value);
+ }
+ default:
+ return value;
+ }
+ }
+
+ case TypeClass.ENUM_value:
+ return readEnumValue(type);
+
+ case TypeClass.STRUCT_value:
+ {
+ Object value = readStructValue(type);
+ return type.hasTypeArguments()
+ ? new Any(new Type(type), value) : value;
+ }
+
+ case TypeClass.EXCEPTION_value:
+ return readExceptionValue(type);
+
+ case TypeClass.INTERFACE_value:
+ {
+ Object value = readInterfaceValue(type);
+ return type.getZClass() == XInterface.class
+ ? value : new Any(new Type(type), value);
+ }
+
+ default:
+ throw new RuntimeException(
+ "Reading ANY with bad type " + type.getTypeClass());
+ }
+ }
+
+ private Object readSequenceValue(TypeDescription type) throws IOException {
+ int len = readCompressedNumber();
+ TypeDescription ctype = type.getComponentType();
+ if (ctype.getTypeClass() == TypeClass.BYTE) {
+ byte[] data = new byte[len];
+ readBytes(data);
+ return data;
+ } else {
+ Object value = Array.newInstance(
+ ctype.getTypeClass() == TypeClass.ANY
+ ? Object.class : ctype.getZClass(), len);
+ for (int i = 0; i < len; ++i) {
+ Array.set(value, i, readValue(ctype));
+ }
+ return value;
+ }
+ }
+
+ private Enum readEnumValue(TypeDescription type) throws IOException {
+ try {
+ return (Enum)
+ type.getZClass().getMethod(
+ "fromInt", new Class[] { int.class }).
+ invoke(null, new Object[] { readLongValue() });
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Object readStructValue(TypeDescription type) {
+ Object value;
+ try {
+ value = type.getZClass().newInstance();
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ }
+ readFields(type, value);
+ return value;
+ }
+
+ private Exception readExceptionValue(TypeDescription type) throws IOException {
+ Exception value;
+ try {
+ value = (Exception)
+ type.getZClass().getConstructor(new Class[] { String.class }).
+ newInstance(new Object[] { readStringValue() });
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ readFields(type, value);
+ return value;
+ }
+
+ private Object readInterfaceValue(TypeDescription type) {
+ return readInterface(new Type(type));
+ }
+
+ private int readCompressedNumber() throws IOException {
+ int number = read8Bit();
+ return number < 0xFF ? number : input.readInt();
+ }
+
+ private void readBytes(byte[] data) throws IOException {
+ input.readFully(data);
+ }
+
+ private void readFields(TypeDescription type, Object value) {
+ FieldDescription[] fields = type.getFieldDescriptions();
+ for (int i = 0; i < fields.length; ++i) {
+ try {
+ fields[i].getField().set(
+ value,
+ readValue(
+ fields[i].getTypeDescription()));
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private final IBridge bridge;
+ private final String[] objectIdCache;
+ private final ThreadId[] threadIdCache;
+ private final TypeDescription[] typeCache;
+ private DataInputStream input;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java b/ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java
new file mode 100644
index 000000000..34cdf7073
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java
@@ -0,0 +1,48 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.protocols.urp;
+
+import com.sun.star.lib.uno.environments.remote.Message;
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.uno.XCurrentContext;
+
+final class UrpMessage extends Message {
+ public UrpMessage(
+ ThreadId threadId, boolean request, String objectId,
+ TypeDescription type, MethodDescription method, boolean synchronous,
+ XCurrentContext currentContext, boolean abnormalTermination,
+ Object result, Object[] arguments, boolean internal)
+ {
+ super(
+ threadId, request, objectId, type, method, synchronous,
+ currentContext, abnormalTermination, result, arguments);
+ this.internal = internal;
+ }
+
+ public boolean isInternal() {
+ return internal;
+ }
+
+ private final boolean internal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/urp.java b/ridljar/com/sun/star/lib/uno/protocols/urp/urp.java
new file mode 100644
index 000000000..0ce9d35be
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/protocols/urp/urp.java
@@ -0,0 +1,760 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.protocols.urp;
+
+import com.sun.star.bridge.InvalidProtocolChangeException;
+import com.sun.star.bridge.ProtocolProperty;
+import com.sun.star.bridge.XProtocolProperties;
+import com.sun.star.lang.DisposedException;
+import com.sun.star.lib.uno.environments.remote.IProtocol;
+import com.sun.star.lib.uno.environments.remote.Message;
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.IBridge;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XCurrentContext;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+/**
+ * This class internally relies on the availability of Java UNO type information
+ * for the interface type <code>com.sun.star.bridge.XProtocolProperties</code>,
+ * even though URP itself does not rely on that type.
+ */
+public final class urp implements IProtocol {
+ public urp(
+ IBridge bridge, String attributes, InputStream input,
+ OutputStream output)
+ {
+ this.input = new DataInputStream(input);
+ this.output = new DataOutputStream(output);
+ marshal = new Marshal(bridge, CACHE_SIZE);
+ unmarshal = new Unmarshal(bridge, CACHE_SIZE);
+ forceSynchronous = parseAttributes(attributes);
+ }
+
+ /**
+ *
+ * @see IProtocol#init
+ */
+ public void init() throws IOException {
+ synchronized (monitor) {
+ if (state == STATE_INITIAL0) {
+ sendRequestChange();
+ }
+ }
+ }
+
+ /**
+ *
+ * @see IProtocol#terminate
+ */
+ public void terminate() {
+ synchronized (monitor) {
+ state = STATE_TERMINATED;
+ initialized = true;
+ monitor.notifyAll();
+ }
+ }
+
+ /**
+ *
+ * @see IProtocol#readMessage
+ */
+ public Message readMessage() throws IOException {
+ for (;;) {
+ if (!unmarshal.hasMore()) {
+ unmarshal.reset(readBlock());
+ if (!unmarshal.hasMore()) {
+ throw new IOException("closeConnection message received");
+ }
+ }
+ UrpMessage msg;
+ int header = unmarshal.read8Bit();
+ if ((header & HEADER_LONGHEADER) != 0) {
+ if ((header & HEADER_REQUEST) != 0) {
+ msg = readLongRequest(header);
+ } else {
+ msg = readReply(header);
+ }
+ } else {
+ msg = readShortRequest(header);
+ }
+ if (msg.isInternal()) {
+ handleInternalMessage(msg);
+ } else {
+ return msg;
+ }
+ }
+ }
+
+ /**
+ *
+ * @see IProtocol#writeRequest
+ */
+ public boolean writeRequest(
+ String oid, TypeDescription type, String function, ThreadId tid,
+ Object[] arguments)
+ throws IOException
+ {
+ if (oid.equals(PROPERTIES_OID)) {
+ throw new IllegalArgumentException("illegal OID " + oid);
+ }
+ synchronized (monitor) {
+ while (!initialized) {
+ try {
+ monitor.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ }
+ if (state == STATE_TERMINATED) {
+ throw new DisposedException();
+ }
+ return writeRequest(false, oid, type, function, tid, arguments);
+ }
+ }
+
+ /**
+ *
+ * @see IProtocol#writeReply
+ */
+ public void writeReply(boolean exception, ThreadId tid, Object result)
+ throws IOException
+ {
+ synchronized (output) {
+ writeQueuedReleases();
+ int header = HEADER_LONGHEADER;
+ PendingRequests.Item pending = pendingIn.pop(tid);
+ TypeDescription resultType;
+ TypeDescription[] argTypes;
+ Object[] args;
+ if (exception) {
+ header |= HEADER_EXCEPTION;
+ resultType = TypeDescription.getTypeDescription(TypeClass.ANY);
+ argTypes = null;
+ args = null;
+ } else {
+ resultType = pending.function.getReturnSignature();
+ argTypes = pending.function.getOutSignature();
+ args = pending.arguments;
+ }
+ if (!tid.equals(outL1Tid)) {
+ header |= HEADER_NEWTID;
+ outL1Tid = tid;
+ } else {
+ tid = null;
+ }
+ marshal.write8Bit(header);
+ if (tid != null) {
+ marshal.writeThreadId(tid);
+ }
+ marshal.writeValue(resultType, result);
+ if (argTypes != null) {
+ for (int i = 0; i < argTypes.length; ++i) {
+ if (argTypes[i] != null) {
+ marshal.writeValue(
+ argTypes[i].getComponentType(),
+ Array.get(args[i], 0));
+ }
+ }
+ }
+ writeBlock(true);
+ }
+ }
+
+ private void sendRequestChange() throws IOException {
+ if (propertiesTid == null) {
+ propertiesTid = ThreadId.createFresh();
+ }
+ random = randomGenerator.nextInt();
+ writeRequest(
+ true, PROPERTIES_OID,
+ TypeDescription.getTypeDescription(XProtocolProperties.class),
+ PROPERTIES_FUN_REQUEST_CHANGE, propertiesTid,
+ new Object[] { Integer.valueOf(random) });
+ state = STATE_REQUESTED;
+ }
+
+ private void handleInternalMessage(Message message) throws IOException {
+ if (message.isRequest()) {
+ String t = message.getType().getTypeName();
+ if (!t.equals("com.sun.star.bridge.XProtocolProperties")) {
+ throw new IOException(
+ "read URP protocol properties request with unsupported"
+ + " type " + t);
+ }
+ int fid = message.getMethod().getIndex();
+ switch (fid) {
+ case PROPERTIES_FID_REQUEST_CHANGE:
+ checkSynchronousPropertyRequest(message);
+ synchronized (monitor) {
+ switch (state) {
+ case STATE_INITIAL0:
+ case STATE_INITIAL:
+ writeReply(
+ false, message.getThreadId(), Integer.valueOf(1));
+ state = STATE_WAIT;
+ break;
+ case STATE_REQUESTED:
+ int n
+ = ((Integer) message.getArguments()[0]).intValue();
+ if (random < n) {
+ writeReply(
+ false, message.getThreadId(), Integer.valueOf(1));
+ state = STATE_WAIT;
+ } else if (random == n) {
+ writeReply(
+ false, message.getThreadId(), Integer.valueOf(-1));
+ state = STATE_INITIAL;
+ sendRequestChange();
+ } else {
+ writeReply(
+ false, message.getThreadId(), Integer.valueOf(0));
+ }
+ break;
+ default:
+ writeReply(
+ true, message.getThreadId(),
+ new com.sun.star.uno.RuntimeException(
+ "read URP protocol properties requestChange"
+ + " request in illegal state"));
+ break;
+ }
+ }
+ break;
+ case PROPERTIES_FID_COMMIT_CHANGE:
+ checkSynchronousPropertyRequest(message);
+ synchronized (monitor) {
+ if (state == STATE_WAIT) {
+ ProtocolProperty[] p = (ProtocolProperty[])
+ message.getArguments()[0];
+ boolean ok = true;
+ boolean cc = false;
+ int i = 0;
+ for (; i < p.length; ++i) {
+ if (p[i].Name.equals(PROPERTY_CURRENT_CONTEXT)) {
+ cc = true;
+ } else {
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ writeReply(false, message.getThreadId(), null);
+ } else {
+ writeReply(
+ true, message.getThreadId(),
+ new InvalidProtocolChangeException(
+ "", null, p[i], 1));
+ }
+ state = STATE_INITIAL;
+ if (!initialized) {
+ if (cc) {
+ currentContext = true;
+ initialized = true;
+ monitor.notifyAll();
+ } else {
+ sendRequestChange();
+ }
+ }
+ } else {
+ writeReply(
+ true, message.getThreadId(),
+ new com.sun.star.uno.RuntimeException(
+ "read URP protocol properties commitChange"
+ + " request in illegal state"));
+ }
+ }
+ break;
+ default:
+ throw new IOException(
+ "read URP protocol properties request with unsupported"
+ + " function ID " + fid);
+ }
+ } else {
+ synchronized (monitor) {
+ if (state == STATE_COMMITTED) {
+ // commitChange reply:
+ if (!message.isAbnormalTermination()) {
+ currentContext = true;
+ }
+ state = STATE_INITIAL;
+ initialized = true;
+ monitor.notifyAll();
+ } else {
+ // requestChange reply:
+ if (message.isAbnormalTermination()) {
+ // remote side probably does not support negotiation:
+ state = STATE_INITIAL;
+ initialized = true;
+ monitor.notifyAll();
+ } else {
+ int n = ((Integer) message.getResult()).intValue();
+ switch (n) {
+ case -1:
+ case 0:
+ break;
+ case 1:
+ writeRequest(
+ true, PROPERTIES_OID,
+ TypeDescription.getTypeDescription(
+ XProtocolProperties.class),
+ PROPERTIES_FUN_COMMIT_CHANGE, propertiesTid,
+ new Object[] {
+ new ProtocolProperty[] {
+ new ProtocolProperty(
+ PROPERTY_CURRENT_CONTEXT,
+ Any.VOID) } });
+ state = STATE_COMMITTED;
+ break;
+ default:
+ throw new IOException(
+ "read URP protocol properties "
+ + PROPERTIES_FUN_REQUEST_CHANGE
+ + " reply with illegal return value " + n);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void checkSynchronousPropertyRequest(Message message)
+ throws IOException
+ {
+ if (!message.isSynchronous()) {
+ throw new IOException(
+ "read URP protocol properties request for synchronous function"
+ + " marked as not SYNCHRONOUS");
+ }
+ }
+
+ private byte[] readBlock() throws IOException {
+ int size = input.readInt();
+ input.readInt(); // ignore count
+ byte[] bytes = new byte[size];
+ input.readFully(bytes);
+ return bytes;
+ }
+
+ private UrpMessage readLongRequest(int header) throws IOException {
+ boolean sync = false;
+ if ((header & HEADER_MOREFLAGS) != 0) {
+ if (unmarshal.read8Bit() != (HEADER_MUSTREPLY | HEADER_SYNCHRONOUS))
+ {
+ throw new IOException(
+ "read URP request with bad MUSTREPLY/SYNCHRONOUS byte");
+ }
+ sync = true;
+ }
+ int funId = (header & HEADER_FUNCTIONID16) != 0
+ ? unmarshal.read16Bit() : unmarshal.read8Bit();
+ if ((header & HEADER_NEWTYPE) != 0) {
+ inL1Type = unmarshal.readType();
+ if (inL1Type.getTypeClass() != TypeClass.INTERFACE) {
+ throw new IOException(
+ "read URP request with non-interface type " + inL1Type);
+ }
+ }
+ if ((header & HEADER_NEWOID) != 0) {
+ inL1Oid = unmarshal.readObjectId();
+ }
+ if ((header & HEADER_NEWTID) != 0) {
+ inL1Tid = unmarshal.readThreadId();
+ }
+ return readRequest(funId, sync);
+ }
+
+ private UrpMessage readShortRequest(int header) throws IOException {
+ int funId = (header & HEADER_FUNCTIONID14) != 0
+ ? ((header & HEADER_FUNCTIONID) << 8) | unmarshal.read8Bit()
+ : header & HEADER_FUNCTIONID;
+ return readRequest(funId, false);
+ }
+
+ private UrpMessage readRequest(int functionId, boolean forcedSynchronous)
+ throws IOException
+ {
+ boolean internal = PROPERTIES_OID.equals(inL1Oid);
+ // inL1Oid may be null in XInstanceProvider.getInstance("")
+ XCurrentContext cc =
+ (currentContext && !internal
+ && functionId != MethodDescription.ID_RELEASE)
+ ? (XCurrentContext) unmarshal.readInterface(
+ new Type(XCurrentContext.class))
+ : null;
+ MethodDescription desc = inL1Type.getMethodDescription(functionId);
+ if (desc == null) {
+ throw new IOException(
+ "read URP request with unsupported function ID " + functionId);
+ }
+ TypeDescription[] inSig = desc.getInSignature();
+ TypeDescription[] outSig = desc.getOutSignature();
+ Object[] args = new Object[inSig.length];
+ for (int i = 0; i < args.length; ++i) {
+ if (inSig[i] != null) {
+ if (outSig[i] != null) {
+ Object inout = Array.newInstance(
+ outSig[i].getComponentType().getZClass(), 1);
+ Array.set(
+ inout, 0,
+ unmarshal.readValue(
+ outSig[i].getComponentType()));
+ args[i] = inout;
+ } else {
+ args[i] = unmarshal.readValue(inSig[i]);
+ }
+ } else {
+ args[i] = Array.newInstance(
+ outSig[i].getComponentType().getZClass(), 1);
+ }
+ }
+ boolean sync = forcedSynchronous || !desc.isOneway();
+ if (sync) {
+ pendingIn.push(
+ inL1Tid, new PendingRequests.Item(internal, desc, args));
+ }
+ return new UrpMessage(
+ inL1Tid, true, inL1Oid, inL1Type, desc, sync, cc, false, null, args,
+ internal);
+ }
+
+ private UrpMessage readReply(int header) {
+ if ((header & HEADER_NEWTID) != 0) {
+ inL1Tid = unmarshal.readThreadId();
+ }
+ PendingRequests.Item pending = pendingOut.pop(inL1Tid);
+ TypeDescription resultType;
+ TypeDescription[] argTypes;
+ Object[] args;
+ boolean exception = (header & HEADER_EXCEPTION) != 0;
+ if (exception) {
+ resultType = TypeDescription.getTypeDescription(TypeClass.ANY);
+ argTypes = null;
+ args = null;
+ } else {
+ resultType = pending.function.getReturnSignature();
+ argTypes = pending.function.getOutSignature();
+ args = pending.arguments;
+ }
+ Object result = resultType == null
+ ? null : unmarshal.readValue(resultType);
+ if (argTypes != null) {
+ for (int i = 0; i < argTypes.length; ++i) {
+ if (argTypes[i] != null) {
+ Array.set(
+ args[i], 0,
+ unmarshal.readValue(
+ argTypes[i].getComponentType()));
+ }
+ }
+ }
+ return new UrpMessage(
+ inL1Tid, false, null, null, null, false, null, exception, result,
+ args, pending.internal);
+ }
+
+ private boolean writeRequest(
+ boolean internal, String oid, TypeDescription type, String function,
+ ThreadId tid, Object[] arguments)
+ throws IOException
+ {
+ MethodDescription desc = type.getMethodDescription(function);
+ synchronized (output) {
+ if (desc.getIndex() == MethodDescription.ID_RELEASE
+ && releaseQueue.size() < MAX_RELEASE_QUEUE_SIZE)
+ {
+ releaseQueue.add(
+ new QueuedRelease(internal, oid, type, desc, tid));
+ return false;
+ } else {
+ writeQueuedReleases();
+ return writeRequest(
+ internal, oid, type, desc, tid, arguments, true);
+ }
+ }
+ }
+
+ private boolean writeRequest(
+ boolean internal, String oid, TypeDescription type,
+ MethodDescription desc, ThreadId tid, Object[] arguments,
+ boolean flush)
+ throws IOException
+ {
+ int funId = desc.getIndex();
+ if (funId < 0 || funId > MAX_FUNCTIONID16) {
+ throw new IllegalArgumentException(
+ "function ID " + funId + " out of range");
+ }
+ boolean forceSync = forceSynchronous
+ && funId != MethodDescription.ID_RELEASE;
+ boolean moreFlags = forceSync && desc.isOneway();
+ boolean longHeader = moreFlags;
+ int header = 0;
+ if (!type.equals(outL1Type)) {
+ longHeader = true;
+ header |= HEADER_NEWTYPE;
+ outL1Type = type;
+ } else {
+ type = null;
+ }
+ if (!oid.equals(outL1Oid)) {
+ longHeader = true;
+ header |= HEADER_NEWOID;
+ outL1Oid = oid;
+ } else {
+ oid = null;
+ }
+ if (!tid.equals(outL1Tid)) {
+ longHeader = true;
+ header |= HEADER_NEWTID;
+ outL1Tid = tid;
+ } else {
+ tid = null;
+ }
+ if (funId > MAX_FUNCTIONID14) {
+ longHeader = true;
+ }
+ if (longHeader) {
+ header |= HEADER_LONGHEADER | HEADER_REQUEST;
+ if (funId > MAX_FUNCTIONID8) {
+ header |= HEADER_FUNCTIONID16;
+ }
+ if (moreFlags) {
+ header |= HEADER_MOREFLAGS;
+ }
+ marshal.write8Bit(header);
+ if (moreFlags) {
+ marshal.write8Bit(HEADER_MUSTREPLY | HEADER_SYNCHRONOUS);
+ }
+ if (funId > MAX_FUNCTIONID8) {
+ marshal.write16Bit(funId);
+ } else {
+ marshal.write8Bit(funId);
+ }
+ if (type != null) {
+ marshal.writeType(type);
+ }
+ if (oid != null) {
+ marshal.writeObjectId(oid);
+ }
+ if (tid != null) {
+ marshal.writeThreadId(tid);
+ }
+ } else {
+ if (funId > HEADER_FUNCTIONID) {
+ marshal.write8Bit(HEADER_FUNCTIONID14 | (funId >> 8));
+ }
+ marshal.write8Bit(funId);
+ }
+ if (currentContext && !internal
+ && funId != MethodDescription.ID_RELEASE)
+ {
+ marshal.writeInterface(
+ UnoRuntime.getCurrentContext(),
+ new Type(XCurrentContext.class));
+ }
+ TypeDescription[] inSig = desc.getInSignature();
+ TypeDescription[] outSig = desc.getOutSignature();
+ for (int i = 0; i < inSig.length; ++i) {
+ if (inSig[i] != null) {
+ if (outSig[i] != null) {
+ marshal.writeValue(
+ outSig[i].getComponentType(),
+ ((Object[]) arguments[i])[0]);
+ } else {
+ marshal.writeValue(
+ inSig[i], arguments[i]);
+ }
+ }
+ }
+ boolean sync = forceSync || !desc.isOneway();
+ if (sync) {
+ pendingOut.push(
+ outL1Tid, new PendingRequests.Item(internal, desc, arguments));
+ }
+ writeBlock(flush);
+ return sync;
+ }
+
+ private void writeBlock(boolean flush) throws IOException {
+ byte[] data = marshal.reset();
+ output.writeInt(data.length);
+ output.writeInt(1);
+ output.write(data);
+ if (flush) {
+ output.flush();
+ }
+ }
+
+ private void writeQueuedReleases() throws IOException {
+ for (int i = releaseQueue.size(); i > 0;) {
+ --i;
+ QueuedRelease r = releaseQueue.get(i);
+ writeRequest(
+ r.internal, r.objectId, r.type, r.method, r.threadId, null,
+ false);
+ releaseQueue.remove(i);
+ }
+ }
+
+ private static boolean parseAttributes(String attributes) {
+ boolean forceSynchronous = true;
+ if (attributes != null) {
+ StringTokenizer t = new StringTokenizer(attributes, ",");
+ while (t.hasMoreTokens()) {
+ String a = t.nextToken();
+ String v = null;
+ int i = a.indexOf('=');
+ if (i >= 0) {
+ v = a.substring(i + 1);
+ a = a.substring(0, i);
+ }
+ if (a.equalsIgnoreCase("ForceSynchronous")) {
+ forceSynchronous = parseBooleanAttributeValue(a, v);
+ } else if (a.equalsIgnoreCase("negotiate")) {
+ // Ignored:
+ parseBooleanAttributeValue(a, v);
+ } else {
+ throw new IllegalArgumentException(
+ "unknown protocol attribute " + a);
+ }
+ }
+ }
+ return forceSynchronous;
+ }
+
+ private static boolean parseBooleanAttributeValue(
+ String attribute, String value)
+ {
+ if (value == null) {
+ throw new IllegalArgumentException(
+ "missing value for protocol attribute " + attribute);
+ }
+ if (value.equals("0")) {
+ return false;
+ } else if (value.equals("1")) {
+ return true;
+ } else {
+ throw new IllegalArgumentException(
+ "bad value " + value + " for protocol attribute " + attribute);
+ }
+ }
+
+ private static final class QueuedRelease {
+ public QueuedRelease(
+ boolean internal, String objectId, TypeDescription type,
+ MethodDescription method, ThreadId threadId)
+ {
+ this.internal = internal;
+ this.objectId = objectId;
+ this.type = type;
+ this.method = method;
+ this.threadId = threadId;
+ }
+
+ public final boolean internal;
+ public final String objectId;
+ public final TypeDescription type;
+ public final MethodDescription method;
+ public final ThreadId threadId;
+ }
+
+ private static final String PROPERTIES_OID = "UrpProtocolProperties";
+ private static final int PROPERTIES_FID_REQUEST_CHANGE = 4;
+ private static final String PROPERTIES_FUN_REQUEST_CHANGE = "requestChange";
+ private static final int PROPERTIES_FID_COMMIT_CHANGE = 5;
+ private static final String PROPERTIES_FUN_COMMIT_CHANGE = "commitChange";
+ private static final String PROPERTY_CURRENT_CONTEXT = "CurrentContext";
+
+ private static final short CACHE_SIZE = 256;
+
+ private static final int HEADER_LONGHEADER = 0x80;
+ private static final int HEADER_REQUEST = 0x40;
+ private static final int HEADER_NEWTYPE = 0x20;
+ private static final int HEADER_NEWOID = 0x10;
+ private static final int HEADER_NEWTID = 0x08;
+ private static final int HEADER_FUNCTIONID16 = 0x04;
+ private static final int HEADER_MOREFLAGS = 0x01;
+ private static final int HEADER_MUSTREPLY = 0x80;
+ private static final int HEADER_SYNCHRONOUS = 0x40;
+ private static final int HEADER_FUNCTIONID14 = 0x40;
+ private static final int HEADER_FUNCTIONID = 0x3F;
+ private static final int HEADER_EXCEPTION = 0x20;
+
+ private static final int MAX_FUNCTIONID16 = 0xFFFF;
+ private static final int MAX_FUNCTIONID14 = 0x3FFF;
+ private static final int MAX_FUNCTIONID8 = 0xFF;
+
+ private static final int STATE_INITIAL0 = 0;
+ private static final int STATE_INITIAL = 1;
+ private static final int STATE_REQUESTED = 2;
+ private static final int STATE_COMMITTED = 3;
+ private static final int STATE_WAIT = 4;
+ private static final int STATE_TERMINATED = 5;
+
+ private static final int MAX_RELEASE_QUEUE_SIZE = 100;
+
+ private static final Random randomGenerator = new Random();
+
+ private final DataInput input;
+ private final DataOutputStream output;
+
+ private final Marshal marshal;
+ private final Unmarshal unmarshal;
+
+ private final boolean forceSynchronous;
+
+ private final PendingRequests pendingIn = new PendingRequests();
+ private final PendingRequests pendingOut = new PendingRequests();
+
+ private final Object monitor = new Object();
+ private int state = STATE_INITIAL0;
+ private boolean initialized = false;
+ private ThreadId propertiesTid = null;
+ private int random;
+ private boolean currentContext = false;
+
+ private ThreadId inL1Tid = null;
+ private String inL1Oid = null;
+ private TypeDescription inL1Type = null;
+
+ private ThreadId outL1Tid = null;
+ private String outL1Oid = null;
+ private TypeDescription outL1Type = null;
+
+ private final ArrayList<QueuedRelease> releaseQueue = new ArrayList<QueuedRelease>(); // of QueuedRelease
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java b/ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java
new file mode 100644
index 000000000..6dcdc99db
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.typedesc;
+
+import java.lang.reflect.Field;
+
+/**
+ * Describes non method members.
+ */
+public final class FieldDescription {
+ public FieldDescription(
+ String name, int index, TypeDescription typeDescription, Field field)
+ {
+ this.name = name;
+ this.index = index;
+ this.typeDescription = typeDescription;
+ this.field = field;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isUnsigned() {
+ return MemberDescriptionHelper.isUnsigned(typeDescription);
+ }
+
+ public boolean isAny() {
+ return MemberDescriptionHelper.isAny(typeDescription);
+ }
+
+ public boolean isInterface() {
+ return MemberDescriptionHelper.isInterface(typeDescription);
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Gives the name of this member.
+ * <p>
+ * @return the name
+ */
+ public TypeDescription getTypeDescription() {
+ return typeDescription;
+ }
+
+ /**
+ * Gives native java field of this member.
+ * <p>
+ * @return the java field
+ */
+ public Field getField() {
+ return field;
+ }
+
+ private final String name;
+ private final int index;
+ private final TypeDescription typeDescription;
+ private final Field field;
+}
diff --git a/ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java b/ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java
new file mode 100644
index 000000000..27a5361f3
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.typedesc;
+
+import com.sun.star.uno.TypeClass;
+
+final class MemberDescriptionHelper {
+ public static boolean isUnsigned(TypeDescription desc) {
+ switch (getElementTypeClass(desc).getValue()) {
+ case TypeClass.UNSIGNED_SHORT_value:
+ case TypeClass.UNSIGNED_LONG_value:
+ case TypeClass.UNSIGNED_HYPER_value:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isAny(TypeDescription desc) {
+ return getElementTypeClass(desc) == TypeClass.ANY;
+ }
+
+ public static boolean isInterface(TypeDescription desc) {
+ return getElementTypeClass(desc) == TypeClass.INTERFACE;
+ }
+
+ private static TypeClass getElementTypeClass(TypeDescription desc) {
+ for (;; desc = desc.getComponentType()) {
+ TypeClass tc = desc.getTypeClass();
+ if (tc != TypeClass.SEQUENCE) {
+ return tc;
+ }
+ }
+ }
+
+ private MemberDescriptionHelper() {} // do not instantiate
+}
diff --git a/ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java b/ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java
new file mode 100644
index 000000000..6cbbb1678
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.typedesc;
+
+import java.lang.reflect.Method;
+
+/**
+ * Allows to examine a method in detail. It gives a view to java methods from a UNO point.
+ */
+public final class MethodDescription {
+ MethodDescription(
+ String name, int index, boolean oneway, TypeDescription[] inSignature,
+ TypeDescription[] outSignature, TypeDescription returnSignature,
+ Method method)
+ {
+ this.name = name;
+ this.index = index;
+ this.oneway = oneway;
+ this.inSignature = inSignature;
+ this.outSignature = outSignature;
+ this.returnSignature = returnSignature;
+ this.method = method;
+ }
+
+ MethodDescription(MethodDescription other, int index) {
+ this(
+ other.getName(), index, other.isOneway(), other.getInSignature(),
+ other.getOutSignature(), other.getReturnSignature(),
+ other.getMethod());
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isUnsigned() {
+ return MemberDescriptionHelper.isUnsigned(returnSignature);
+ }
+
+ public boolean isAny() {
+ return MemberDescriptionHelper.isAny(returnSignature);
+ }
+
+ public boolean isInterface() {
+ return MemberDescriptionHelper.isInterface(returnSignature);
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Indicates if this method is <code>oneWay</code>,
+ * respectively if this method may become executed asynchronously.
+ * @return true means may execute asynchronously .
+ */
+ public boolean isOneway() {
+ return oneway;
+ }
+
+ /**
+ * Indicates if this method is const.
+ * @return true means it is const.
+ */
+ public boolean isConst() {
+ return false;
+ }
+
+ /**
+ * Gives any array of <code>TypeDescription</code> of
+ * the [in] parameters.
+ * @return the in parameters
+ */
+ public TypeDescription[] getInSignature() {
+ return inSignature;
+ }
+
+ /**
+ * Gives any array of <code>TypeDescription</code> of
+ * the [out] parameters.
+ * @return the out parameters
+ */
+ public TypeDescription[] getOutSignature() {
+ return outSignature;
+ }
+
+ /**
+ * Gives the <code>TypeDescription</code> of
+ * the return type.
+ * @return the return type <code>TypeDescription</code>
+ */
+ public TypeDescription getReturnSignature() {
+ return returnSignature;
+ }
+
+ /**
+ * Gives native java method of this method.
+ * @return the java method
+ */
+ public Method getMethod() {
+ return method;
+ }
+
+ public static final int ID_QUERY_INTERFACE = 0;
+ public static final int ID_ACQUIRE = 1;
+ public static final int ID_RELEASE = 2;
+
+ private final String name;
+ private final int index;
+ private final boolean oneway;
+ private final TypeDescription[] inSignature;
+ private final TypeDescription[] outSignature;
+ private final TypeDescription returnSignature;
+ private final Method method;
+}
diff --git a/ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java b/ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java
new file mode 100644
index 000000000..a3c3bcd52
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java
@@ -0,0 +1,819 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.uno.typedesc;
+
+import com.sun.star.lib.uno.typeinfo.AttributeTypeInfo;
+import com.sun.star.lib.uno.typeinfo.MemberTypeInfo;
+import com.sun.star.lib.uno.typeinfo.MethodTypeInfo;
+import com.sun.star.lib.uno.typeinfo.ParameterTypeInfo;
+import com.sun.star.lib.uno.typeinfo.TypeInfo;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Supplies information about UNO types. Allows to examine a type
+ * in detail (e.g. it is used for marshaling/unmarshaling).
+ *
+ * @since UDK2.0
+ */
+public final class TypeDescription {
+ public static TypeDescription getTypeDescription(String typeName)
+ throws ClassNotFoundException
+ {
+ Type t = new Type(typeName);
+ if (t.getTypeClass() == TypeClass.UNKNOWN) {
+ if (typeName.startsWith("[]")) {
+ t = new Type(typeName, TypeClass.SEQUENCE);
+ } else {
+ t = new Type(Class.forName(typeName));
+ }
+ }
+ return get(t);
+ }
+
+ public static TypeDescription getTypeDescription(Class<?> zClass) {
+ return getDefinitely(new Type(zClass));
+ }
+
+ public static TypeDescription getTypeDescription(Type type)
+ throws ClassNotFoundException
+ {
+ //TODO: synchronize on type?
+ TypeDescription desc = type.getTypeDescription();
+ if (desc == null) {
+ desc = getTypeDescription(type.getTypeName());
+ type.setTypeDescription(desc);
+ }
+ return desc;
+ }
+
+ public static TypeDescription getTypeDescription(TypeClass typeClass) {
+ switch (typeClass.getValue()) {
+ case TypeClass.VOID_value:
+ return getDefinitely(Type.VOID);
+
+ case TypeClass.BOOLEAN_value:
+ return getDefinitely(Type.BOOLEAN);
+
+ case TypeClass.BYTE_value:
+ return getDefinitely(Type.BYTE);
+
+ case TypeClass.SHORT_value:
+ return getDefinitely(Type.SHORT);
+
+ case TypeClass.UNSIGNED_SHORT_value:
+ return getDefinitely(Type.UNSIGNED_SHORT);
+
+ case TypeClass.LONG_value:
+ return getDefinitely(Type.LONG);
+
+ case TypeClass.UNSIGNED_LONG_value:
+ return getDefinitely(Type.UNSIGNED_LONG);
+
+ case TypeClass.HYPER_value:
+ return getDefinitely(Type.HYPER);
+
+ case TypeClass.UNSIGNED_HYPER_value:
+ return getDefinitely(Type.UNSIGNED_HYPER);
+
+ case TypeClass.FLOAT_value:
+ return getDefinitely(Type.FLOAT);
+
+ case TypeClass.DOUBLE_value:
+ return getDefinitely(Type.DOUBLE);
+
+ case TypeClass.CHAR_value:
+ return getDefinitely(Type.CHAR);
+
+ case TypeClass.STRING_value:
+ return getDefinitely(Type.STRING);
+
+ case TypeClass.TYPE_value:
+ return getDefinitely(Type.TYPE);
+
+ case TypeClass.ANY_value:
+ return getDefinitely(Type.ANY);
+
+ default:
+ return null;
+ }
+ }
+
+ public static boolean isTypeClassSimple(TypeClass typeClass) {
+ return getTypeDescription(typeClass) != null;
+ }
+
+ /**
+ * Gets the <code>TypeDescription</code> of the
+ * super, if it exists.
+ * @return the <code>TypeDescription</code>.
+ */
+ public TypeDescription getSuperType() {
+ // Arbitrarily take the first super type:
+ return superTypes == null || superTypes.length == 0
+ ? null : superTypes[0];
+ }
+
+ /**
+ * Gets the <code>MethodDescription</code> for every
+ * method, if this type is an interface. Otherwise
+ * returns <code>null</code>.
+ * @return the <code>MethodDescription[]</code>.
+ */
+ public MethodDescription[] getMethodDescriptions() {
+ initMethodDescriptions();
+ return methodDescriptions; //TODO: clone?
+ }
+
+ /**
+ * Gets the <code>MethodDescription</code> for the
+ * method with index methodId, if it exists, otherwise
+ * returns <code>null</code>.
+ *
+ * @param methodId the index.
+ *
+ * @return the <code>MethodDescription</code>.
+ */
+ public MethodDescription getMethodDescription(int methodId) {
+ initMethodDescriptions();
+ return methodId < 0
+ ? null
+ : methodId < superMethodDescriptions.length
+ ? superMethodDescriptions[methodId]
+ : (methodId - superMethodDescriptions.length
+ < methodDescriptions.length)
+ ? methodDescriptions[methodId - superMethodDescriptions.length]
+ : null;
+ }
+
+ /**
+ * Gets the <code>MethodDescription</code> for the
+ * method with the name <code>name</code>, if it exists,
+ * otherwise returns <code>null</code>.
+ *
+ * @param name the name of the method.
+ *
+ * @return the <code>MethodDescription</code>.
+ */
+ public MethodDescription getMethodDescription(String name) {
+ initMethodDescriptions();
+ for (int i = 0; i < superMethodDescriptions.length; ++i) {
+ if (superMethodDescriptions[i].getName().equals(name)) {
+ return superMethodDescriptions[i];
+ }
+ }
+ for (int i = 0; i < methodDescriptions.length; ++i) {
+ if (methodDescriptions[i].getName().equals(name)) {
+ return methodDescriptions[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the <code>FieldDescription</code> for every
+ * field, if this type is an interface. Otherwise
+ * returns <code>null</code>.
+ * @return the <code>FieldDescription[]</code>.
+ */
+ public FieldDescription[] getFieldDescriptions() {
+ return fieldDescriptions; //TODO: clone?
+ }
+
+ /**
+ * Gets the <code>FieldDescription</code> for the
+ * field with the name <code>name</code>, if it exists,
+ * otherwise returns <code>null</code>.
+ *
+ * @param name the name of the field.
+ *
+ * @return the <code>FieldDescription</code>.
+ */
+ public FieldDescription getFieldDescription(String name) {
+ for (int i = 0; i < fieldDescriptions.length; ++i) {
+ if (fieldDescriptions[i].getName().equals(name)) {
+ return fieldDescriptions[i];
+ }
+ }
+ return superTypes != null && superTypes.length == 1
+ ? superTypes[0].getFieldDescription(name) : null;
+ }
+
+ /**
+ * Gets the IDL <code>TypeClass</code> of the type.
+ * @return the <code>TypeClass</code>.
+ */
+ public TypeClass getTypeClass() {
+ return typeClass;
+ }
+
+ /**
+ * Gets the component <code>TypeDescription</code> if
+ * this is an array type, otherwise returns <code>null</code>.
+ * @return the <code>TypeDescription</code>
+ */
+ public TypeDescription getComponentType() {
+ return componentType;
+ }
+
+ /**
+ * Gets the (UNO) type name.
+ * <table>
+ * <caption>Mapping from UNO types to type names</caption>
+ * <thead>
+ * <tr><th>UNO type</th><th>type name</th></tr>
+ * </thead>
+ * <tbody>
+ * <tr><td>VOID</td><td><code>"void"</code></td></tr>
+ * <tr><td>BOOLEAN</td><td><code>"boolean"</code></td></tr>
+ * <tr><td>CHAR</td><td><code>"char"</code></td></tr>
+ * <tr><td>BYTE</td><td><code>"byte"</code></td></tr>
+ * <tr><td>SHORT</td><td><code>"short"</code></td></tr>
+ * <tr>
+ * <td>UNSIGNED SHORT</td><td><code>"unsigned short"</code></td>
+ * </tr>
+ * <tr><td>LONG</td><td><code>"long"</code></td></tr>
+ * <tr><td>UNSIGNED LONG</td><td><code>"unsigned long"</code></td></tr>
+ * <tr><td>HYPER</td><td><code>"hyper"</code></td></tr>
+ * <tr>
+ * <td>UNSIGNED HYPER</td><td><code>"unsigned hyper"</code></td>
+ * </tr>
+ * <tr><td>FLOAT</td><td><code>"float"</code></td></tr>
+ * <tr><td>DOUBLE</td><td><code>"double"</code></td></tr>
+ * <tr><td>STRING</td><td><code>"string"</code></td></tr>
+ * <tr><td>TYPE</td><td><code>"type"</code></td></tr>
+ * <tr><td>ANY</td><td><code>"any"</code></td></tr>
+ * <tr>
+ * <td>sequence type of base type <var>T</var></td>
+ * <td><code>"[]"</code> followed by type name for <var>T</var></td>
+ * </tr>
+ * <tr>
+ * <td>enum type named <var>N</var></td>
+ * <td><var>N</var> (see below)</td>
+ * </tr>
+ * <tr>
+ * <td>struct type named <var>N</var></td>
+ * <td><var>N</var> (see below)</td>
+ * </tr>
+ * <tr>
+ * <td>exception type named <var>N</var></td>
+ * <td><var>N</var> (see below)</td>
+ * </tr>
+ * <tr>
+ * <td>interface type named <var>N</var></td>
+ * <td><var>N</var> (see below)</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>For a UNO type named <var>N</var>, consisting of a sequence of module
+ * names <var>M<sub>1</sub></var>, ..., <var>M<sub>n</sub></var> followed by
+ * a simple name <var>S</var>, the corresponding type name consists of the
+ * same sequence of module names and simple name, with <code>"."</code>
+ * separating the individual elements.</p>
+ * @return the type name.
+ */
+ public String getTypeName() {
+ return typeName;
+ }
+
+ /**
+ * Gets the (Java) array type name.
+ * <p>The array type name is defined to be the Java class name (as returned
+ * by <code>Class.forName</code>) of the Java array class that corresponds
+ * to the UNO sequence type with this type (the UNO type represented by this
+ * <code>TypeDescription</code> instance) as base type. For an
+ * <code>TypeDescription</code> instance representing the UNO type VOID,
+ * the array type name is defined to be
+ * <code>"[Ljava.lang.Void;"</code>.</p>
+ * @return the array type name.
+ */
+ public String getArrayTypeName() {
+ return arrayTypeName;
+ }
+
+ /**
+ * Gets the corresponding java class for the type.
+ * @return the corresponding java class.
+ */
+ public Class<?> getZClass() {
+ return zClass;
+ }
+
+ public boolean hasTypeArguments() {
+ return hasTypeArguments;
+ }
+
+ // @see Object#toString
+ @Override
+ public String toString() {
+ return "[" + getClass().getName() + ": " + getTypeClass() + ", "
+ + getTypeName() + "]";
+ }
+
+ private static TypeDescription getDefinitely(Type type) {
+ try {
+ return get(type);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("this cannot happen: " + e);
+ }
+ }
+
+ private static TypeDescription get(Type type) throws ClassNotFoundException
+ {
+ String typeName = type.getTypeName();
+ TypeDescription desc = cache.get(typeName);
+ if (desc == null) {
+ desc = create(type);
+ cache.put(desc);
+ }
+ return desc;
+ }
+
+ private static TypeDescription create(Type type)
+ throws ClassNotFoundException
+ {
+ TypeClass typeClass = type.getTypeClass();
+ String typeName = type.getTypeName();
+ Class<?> zClass = type.getZClass();
+ if (zClass == null) {
+ throw new ClassNotFoundException("UNO type " + type);
+ }
+ switch (typeClass.getValue()) {
+ case TypeClass.VOID_value:
+ return new TypeDescription(
+ typeClass, typeName, "[Ljava.lang.Void;", zClass, null, null);
+
+ case TypeClass.BOOLEAN_value:
+ return new TypeDescription(
+ typeClass, typeName, "[Z", zClass, null, null);
+
+ case TypeClass.BYTE_value:
+ return new TypeDescription(
+ typeClass, typeName, "[B", zClass, null, null);
+
+ case TypeClass.SHORT_value:
+ case TypeClass.UNSIGNED_SHORT_value:
+ return new TypeDescription(
+ typeClass, typeName, "[S", zClass, null, null);
+
+ case TypeClass.LONG_value:
+ case TypeClass.UNSIGNED_LONG_value:
+ return new TypeDescription(
+ typeClass, typeName, "[I", zClass, null, null);
+
+ case TypeClass.HYPER_value:
+ case TypeClass.UNSIGNED_HYPER_value:
+ return new TypeDescription(
+ typeClass, typeName, "[J", zClass, null, null);
+
+ case TypeClass.FLOAT_value:
+ return new TypeDescription(
+ typeClass, typeName, "[F", zClass, null, null);
+
+ case TypeClass.DOUBLE_value:
+ return new TypeDescription(
+ typeClass, typeName, "[D", zClass, null, null);
+
+ case TypeClass.CHAR_value:
+ return new TypeDescription(
+ typeClass, typeName, "[C", zClass, null, null);
+
+ case TypeClass.STRING_value:
+ return new TypeDescription(
+ typeClass, typeName, "[Ljava.lang.String;", zClass, null, null);
+
+ case TypeClass.TYPE_value:
+ return new TypeDescription(
+ typeClass, typeName, "[Lcom.sun.star.uno.Type;", zClass, null,
+ null);
+
+ case TypeClass.ANY_value:
+ return new TypeDescription(
+ typeClass, typeName, "[Ljava.lang.Object;", zClass, null, null);
+
+ case TypeClass.SEQUENCE_value:
+ {
+ // assert typeName.startsWith("[]");
+ TypeDescription componentType = getTypeDescription(
+ typeName.substring("[]".length()));
+ // assert zClass.getName().startsWith("[");
+ return new TypeDescription(
+ typeClass, typeName, "[" + zClass.getName(), zClass, null,
+ componentType);
+ }
+
+ case TypeClass.ENUM_value:
+ // assert !zClass.getName().startsWith("[");
+ return new TypeDescription(
+ typeClass, typeName, "[L" + zClass.getName() + ";", zClass,
+ null, null);
+
+ case TypeClass.STRUCT_value:
+ {
+ // This code exploits the fact that an instantiated polymorphic
+ // struct type may not be the direct base of a struct type:
+ Class<?> superClass = zClass.getSuperclass();
+ TypeDescription[] superTypes = superClass != Object.class
+ ? new TypeDescription[] { get(new Type(superClass)) }
+ : null;
+ // assert !zClass.getName().startsWith("[");
+ return new TypeDescription(
+ typeClass, typeName, "[L" + zClass.getName() + ";", zClass,
+ superTypes, null);
+ }
+
+ case TypeClass.EXCEPTION_value:
+ {
+ TypeDescription[] superTypes
+ = typeName.equals("com.sun.star.uno.Exception")
+ || typeName.equals("com.sun.star.uno.RuntimeException")
+ ? null
+ : new TypeDescription[] {
+ get(new Type(zClass.getSuperclass())) };
+ // assert !zClass.getName().startsWith("[");
+ return new TypeDescription(
+ typeClass, typeName, "[L" + zClass.getName() + ";", zClass,
+ superTypes, null);
+ }
+
+ case TypeClass.INTERFACE_value:
+ {
+ List superTypes = new List();
+ Class<?>[] interfaces = zClass.getInterfaces();
+ for (int i = 0; i < interfaces.length; ++i) {
+ Type t = new Type(interfaces[i]);
+ if (t.getTypeClass() == TypeClass.INTERFACE) {
+ TypeDescription desc = getDefinitely(t);
+ TypeDescription[] descs = desc.superTypes;
+ for (int j = 0; j < descs.length; ++j) {
+ superTypes.add(descs[j]);
+ }
+ superTypes.add(desc);
+ }
+ }
+ // assert !zClass.getName().startsWith("[");
+ return new TypeDescription(
+ typeClass, typeName, "[L" + zClass.getName() + ";", zClass,
+ superTypes.toArray(), null);
+ }
+
+ default:
+ throw new IllegalArgumentException("given type has bad type class");
+ }
+ }
+
+ private TypeDescription(
+ TypeClass typeClass, String typeName, String arrayTypeName,
+ Class<?> zClass, TypeDescription[] superTypes,
+ TypeDescription componentType)
+ {
+ this.typeClass = typeClass;
+ this.typeName = typeName;
+ this.arrayTypeName = arrayTypeName;
+ this.zClass = zClass;
+ this.superTypes = superTypes;
+ this.componentType = componentType;
+ TypeDescription[] args = calculateTypeArguments();
+ this.hasTypeArguments = args != null;
+ this.fieldDescriptions = calculateFieldDescriptions(args);
+ // methodDescriptions must be initialized lazily, to avoid problems with
+ // circular dependencies (a super-interface that has a sub-interface as
+ // method parameter type; an interface that has a struct as method
+ // parameter type, and the struct has the interface as member type)
+ }
+
+ private synchronized void initMethodDescriptions() {
+ if (methodDescriptions != null || typeClass != TypeClass.INTERFACE) {
+ return;
+ }
+ if (superTypes.length == 0) { // com.sun.star.uno.XInterface
+ superMethodDescriptions = new MethodDescription[0];
+ methodDescriptions = new MethodDescription[] {
+ new MethodDescription(
+ "queryInterface", MethodDescription.ID_QUERY_INTERFACE,
+ false, new TypeDescription[] { getDefinitely(Type.TYPE) },
+ new TypeDescription[] { null }, getDefinitely(Type.ANY),
+ null),
+ new MethodDescription(
+ "acquire", MethodDescription.ID_ACQUIRE, true,
+ new TypeDescription[0], new TypeDescription[0],
+ getDefinitely(Type.VOID), null),
+ new MethodDescription(
+ "release", MethodDescription.ID_RELEASE, true,
+ new TypeDescription[0], new TypeDescription[0],
+ getDefinitely(Type.VOID), null) };
+ } else {
+ int methodOffset = 0;
+ ArrayList<MethodDescription> superList = new ArrayList<MethodDescription>();
+ for (int i = 0; i < superTypes.length; ++i) {
+ MethodDescription[] ds = superTypes[i].getMethodDescriptions();
+ for (int j = 0; j < ds.length; ++j) {
+ superList.add(new MethodDescription(ds[j], methodOffset++));
+ }
+ }
+ superMethodDescriptions = superList.toArray(
+ new MethodDescription[superList.size()]);
+ ArrayList<MethodDescription> directList = new ArrayList<MethodDescription>();
+ TypeInfo[] infos = getTypeInfo();
+ int infoCount = infos == null ? 0 : infos.length;
+ int index = 0;
+ Method[] methods = zClass.getDeclaredMethods();
+ for (int i = 0; i < infoCount;) {
+ if (infos[i] instanceof AttributeTypeInfo) {
+ AttributeTypeInfo info = (AttributeTypeInfo) infos[i++];
+ if (info.getIndex() != index) {
+ throw new IllegalArgumentException(
+ "Bad UNOTYPEINFO for " + zClass
+ + ": entries not ordererd");
+ }
+ String getterName = "get" + info.getName();
+ Method getter = findMethod(methods, getterName);
+ Type t = info.getUnoType();
+ TypeDescription type = t == null
+ ? getTypeDescription(getter.getReturnType(), info)
+ : getDefinitely(t);
+ directList.add(
+ new MethodDescription(
+ getterName, index++ + methodOffset, false,
+ new TypeDescription[0], new TypeDescription[0],
+ type, getter));
+ if (!info.isReadOnly()) {
+ String setterName = "set" + info.getName();
+ Method setter = findMethod(methods, setterName);
+ directList.add(
+ new MethodDescription(
+ setterName, index++ + methodOffset, false,
+ new TypeDescription[] { type },
+ new TypeDescription[] { null },
+ getDefinitely(Type.VOID), setter));
+ }
+ } else {
+ MethodTypeInfo info = (MethodTypeInfo) infos[i++];
+ if (info.getIndex() != index) {
+ throw new IllegalArgumentException(
+ "Bad UNOTYPEINFO for " + zClass
+ + ": entries not ordererd");
+ }
+ Method method = findMethod(methods, info.getName());
+ Class<?>[] params = method.getParameterTypes();
+ TypeDescription[] in = new TypeDescription[params.length];
+ TypeDescription[] out
+ = new TypeDescription[params.length];
+ for (int j = 0; j < params.length; ++j) {
+ ParameterTypeInfo p = null;
+ if (i < infoCount
+ && infos[i] instanceof ParameterTypeInfo
+ && ((ParameterTypeInfo) infos[i]).getIndex() == j)
+ {
+ p = (ParameterTypeInfo) infos[i++];
+ }
+ Type pt = p == null ? null : p.getUnoType();
+ TypeDescription d = pt == null
+ ? getTypeDescription(params[j], p)
+ : getDefinitely(pt);
+ if (p == null || p.isIN()) {
+ in[j] = d;
+ }
+ if (p != null && p.isOUT()) {
+ out[j] = d;
+ }
+ }
+ Type t = info.getUnoType();
+ directList.add(
+ new MethodDescription(
+ info.getName(), index++ + methodOffset,
+ info.isOneway(), in, out,
+ (t == null
+ ? getTypeDescription(method.getReturnType(), info)
+ : getDefinitely(t)),
+ method));
+ }
+ }
+ methodDescriptions = directList.toArray(
+ new MethodDescription[directList.size()]);
+ }
+ }
+
+ private TypeDescription[] calculateTypeArguments() {
+ if (typeClass != TypeClass.STRUCT) {
+ return null;
+ }
+ int i = typeName.indexOf('<');
+ if (i < 0) {
+ return null;
+ }
+ java.util.List<TypeDescription> args = new java.util.ArrayList<TypeDescription>();
+ do {
+ ++i; // skip '<' or ','
+ int j = i;
+ loop:
+ for (int level = 0; j != typeName.length(); ++j) {
+ switch (typeName.charAt(j)) {
+ case ',':
+ if (level == 0) {
+ break loop;
+ }
+ break;
+
+ case '<':
+ ++level;
+ break;
+
+ case '>':
+ if (level == 0) {
+ break loop;
+ }
+ --level;
+ break;
+ }
+ }
+ if (j != typeName.length()) {
+ Type t = new Type(typeName.substring(i, j));
+ if (t.getZClass() == null) {
+ throw new IllegalArgumentException(
+ "UNO type name \"" + typeName
+ + "\" contains bad type argument \""
+ + typeName.substring(i, j) + "\"");
+ }
+ args.add(getDefinitely(t));
+ }
+ i = j;
+ } while (i != typeName.length() && typeName.charAt(i) != '>');
+ if (i != typeName.length() - 1 || typeName.charAt(i) != '>'
+ || args.isEmpty())
+ {
+ throw new IllegalArgumentException(
+ "UNO type name \"" + typeName + "\" is syntactically invalid");
+ }
+ return args.toArray(
+ new TypeDescription[args.size()]);
+ }
+
+ private FieldDescription[] calculateFieldDescriptions(
+ TypeDescription[] typeArguments)
+ {
+ if (typeClass != TypeClass.STRUCT && typeClass != TypeClass.EXCEPTION) {
+ return null;
+ }
+ TypeInfo[] infos = getTypeInfo();
+ int infoCount = infos == null ? 0 : infos.length;
+ TypeDescription superType = getSuperType();
+ FieldDescription[] superDescs = superType == null
+ ? null : superType.getFieldDescriptions();
+ int superCount = superDescs == null ? 0 : superDescs.length;
+ FieldDescription[] descs = new FieldDescription[
+ superCount + infoCount];
+ if (superCount != 0) {
+ System.arraycopy(superDescs, 0, descs, 0, superCount);
+ }
+ for (int i = 0; i < infoCount; ++i) {
+ MemberTypeInfo info = (MemberTypeInfo) infos[i];
+ if (info.getIndex() != i) {
+ throw new IllegalArgumentException(
+ "Bad UNOTYPEINFO for " + zClass + ": entries not ordererd");
+ }
+ Field field;
+ try {
+ field = zClass.getDeclaredField(info.getName());
+ } catch (NoSuchFieldException e) {
+ throw new IllegalArgumentException(
+ "Bad UNOTYPEINFO for " + zClass + ": " + e);
+ }
+ Type t = info.getUnoType();
+ int index = info.getTypeParameterIndex();
+ descs[i + superCount] = new FieldDescription(
+ info.getName(), i + superCount,
+ (index >= 0
+ ? typeArguments[index]
+ : t == null
+ ? getTypeDescription(field.getType(), info)
+ : getDefinitely(t)),
+ field);
+ }
+ return descs;
+ }
+
+ private TypeInfo[] getTypeInfo() {
+ try {
+ return (TypeInfo[])
+ zClass.getDeclaredField("UNOTYPEINFO").get(null);
+ } catch (NoSuchFieldException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(
+ "Bad UNOTYPEINFO for " + zClass + ": " + e);
+ }
+ }
+
+ private Method findMethod(Method[] methods, String name) {
+ for (int i = 0; i < methods.length; ++i) {
+ if (methods[i].getName().equals(name)) {
+ return methods[i];
+ }
+ }
+ throw new IllegalArgumentException(
+ "Bad UNOTYPEINFO for " + zClass + ": no method " + name);
+ }
+
+ private static TypeDescription getTypeDescription(
+ Class<?> zClass, TypeInfo typeInfo)
+ {
+ return getDefinitely(
+ new Type(
+ zClass,
+ typeInfo != null
+ && (typeInfo.isUnsigned() || typeInfo.isInterface())));
+ }
+
+ private static final class List {
+
+ public void add(TypeDescription desc) {
+ if (!list.contains(desc)) {
+ list.add(desc);
+ }
+ }
+
+ public TypeDescription[] toArray() {
+ return list.toArray(
+ new TypeDescription[list.size()]);
+ }
+
+ private final ArrayList<TypeDescription> list = new ArrayList<TypeDescription>();
+ }
+
+ private static final class Cache {
+
+ public TypeDescription get(String typeName) {
+ synchronized (map) {
+ cleanUp();
+ Entry e = map.get(typeName);
+ return e == null ? null : (TypeDescription) e.get();
+ }
+ }
+
+ public void put(TypeDescription desc) {
+ synchronized (map) {
+ cleanUp();
+ map.put(desc.getTypeName(), new Entry(desc, queue));
+ }
+ }
+
+ private void cleanUp() {
+ for (;;) {
+ Object tmp = queue.poll();
+ Entry e = (Entry)tmp;
+ if (e == null) {
+ break;
+ }
+ map.remove(e.typeName);
+ }
+ }
+
+ private static final class Entry extends SoftReference<TypeDescription> {
+ public Entry(TypeDescription desc, ReferenceQueue<TypeDescription> queue) {
+ super(desc, queue);
+ typeName = desc.getTypeName();
+ }
+
+ public final String typeName;
+ }
+
+ private final HashMap<String, Entry> map = new HashMap<String, Entry>();
+ private final ReferenceQueue<TypeDescription> queue = new ReferenceQueue<TypeDescription>();
+ }
+
+ private static final Cache cache = new Cache();
+
+ private final TypeClass typeClass;
+ private final String typeName;
+ private final String arrayTypeName;
+ private final Class<?> zClass;
+ private final TypeDescription[] superTypes;
+ private final TypeDescription componentType;
+ private final boolean hasTypeArguments;
+ private final FieldDescription[] fieldDescriptions;
+ private MethodDescription[] methodDescriptions = null;
+ private MethodDescription[] superMethodDescriptions;
+}
diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java
new file mode 100644
index 000000000..3230528e1
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.typeinfo;
+
+import com.sun.star.uno.Type;
+
+public class AttributeTypeInfo extends TypeInfo
+{
+ protected int m_index;
+ private final Type m_unoType; // @since UDK 3.2
+
+ /**
+ Create an attribute type info with a UNO type that cannot unambiguously
+ be represented as a Java&nbsp;1.2 type.
+
+ @param name the name of this attribute; must not be <code>null</code>
+
+ @param index the index among the direct members
+
+ @param flags any flags (<code>READONLY</code>, <code>BOUND</code>,
+ <code>UNSIGNED</code>, <code>ANY</code>, <code>INTERFACE</code>)
+
+ @param unoType the exact UNO type; or <code>null</code> if the UNO type
+ is already unambiguously represented by the Java&nbsp;1.2 type
+
+ @since UDK 3.2
+ */
+ public AttributeTypeInfo(String name, int index, int flags, Type unoType) {
+ super(name, flags);
+ m_index = index;
+ m_unoType = unoType;
+ }
+
+ public AttributeTypeInfo(String name, int index, int flags)
+ {
+ this(name, index, flags, null);
+ }
+
+ public int getIndex()
+ {
+ return m_index;
+ }
+
+ public boolean isReadOnly()
+ {
+ return (m_flags & TypeInfo.READONLY) != 0;
+ }
+
+ /**
+ Returns the status of the 'bound' flag.
+
+ @since UDK 3.2
+ */
+ public final boolean isBound() {
+ return (m_flags & TypeInfo.BOUND) != 0;
+ }
+
+ /**
+ Get the exact UNO type of this attribute type info, in case it cannot
+ unambiguously be represented as a Java&nbsp;1.2 type.
+
+ @return the exact UNO type of this attribute type info, or
+ <code>null</code> if the UNO type is already unambiguously represented by
+ the Java&nbsp;1.2 type
+
+ @since UDK 3.2
+ */
+ public final Type getUnoType() {
+ return m_unoType;
+ }
+}
diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java
new file mode 100644
index 000000000..2676718cf
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.typeinfo;
+
+/**
+ @deprecated <code>UNOTYPEINFO</code> for constants is not needed
+ */
+@Deprecated
+public class ConstantTypeInfo extends TypeInfo
+{
+ public ConstantTypeInfo(String name, int flags)
+ {
+ super(name, flags);
+ }
+}
+
+
diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java
new file mode 100644
index 000000000..5b3f669fe
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.typeinfo;
+
+import com.sun.star.uno.Type;
+
+public class MemberTypeInfo extends TypeInfo
+{
+ int m_index;
+ private final Type m_unoType; // @since UDK 3.2
+ private final int m_typeParameterIndex; // @since UDK 3.2
+
+ /**
+ Create a member type info with a UNO type that cannot unambiguously be
+ represented as a Java&nbsp;1.2 type.
+
+ @param name the name of this member; must not be <code>null</code>
+
+ @param index the index among the direct members
+
+ @param flags any flags (<code>UNSIGNED</code>, <code>ANY</code>,
+ <code>INTERFACE</code>, <code>TYPE_PARAMETER</code>)
+
+ @param unoType the exact UNO type; or <code>null</code> if the UNO type
+ is already unambiguously represented by the Java&nbsp;1.2 type
+
+ @param typeParameterIndex the index of the type parameter that determines
+ the type of this parameterized member; or <code>-1</code> if this member
+ is of an explicit type, or is the member of a plain struct type
+
+ @since UDK 3.2
+ */
+ public MemberTypeInfo(
+ String name, int index, int flags, Type unoType, int typeParameterIndex)
+ {
+ super(name, flags);
+ m_index = index;
+ m_unoType = unoType;
+ m_typeParameterIndex = typeParameterIndex;
+ }
+
+ public MemberTypeInfo(String name, int index, int flags )
+ {
+ this(name, index, flags, null, -1);
+ }
+
+ public int getIndex()
+ {
+ return m_index;
+ }
+
+ /**
+ Get the exact UNO type of this member type info, in case it cannot
+ unambiguously be represented as a Java&nbsp;1.2 type.
+
+ @return the exact UNO type of this member type info, or <code>null</code>
+ if the UNO type is already unambiguously represented by the Java&nbsp;1.2
+ type
+
+ @since UDK 3.2
+ */
+ public final Type getUnoType() {
+ return m_unoType;
+ }
+
+ /**
+ Returns the index of the type parameter that determines the parameterized
+ type of this member.
+
+ @return the index of the type parameter that determines the type of this
+ parameterized member; if this member is of an explicit type, or is the
+ member of a plain struct type, <code>-1</code> is returned
+
+ @since UDK 3.2
+ */
+ public final int getTypeParameterIndex() {
+ return m_typeParameterIndex;
+ }
+}
+
+
diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java
new file mode 100644
index 000000000..319e63514
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.typeinfo;
+
+import com.sun.star.uno.Type;
+
+public class MethodTypeInfo extends TypeInfo
+{
+ protected int m_index;
+ private final Type m_unoType; // @since UDK 3.2
+
+ /**
+ Create a method type info with a UNO return type that cannot
+ unambiguously be represented as a Java&nbsp;1.2 type.
+
+ @param name the name of this method; must not be <code>null</code>
+
+ @param index the index among the direct members
+
+ @param flags any flags (<code>ONEWAY</code>, <code>UNSIGNED</code>,
+ <code>ANY</code>, <code>INTERFACE</code>)
+
+ @param unoType the exact UNO return type; or <code>null</code> if the UNO
+ type is already unambiguously represented by the Java&nbsp;1.2 type
+
+ @since UDK 3.2
+ */
+ public MethodTypeInfo(String name, int index, int flags, Type unoType) {
+ super(name, flags);
+ m_index = index;
+ m_unoType = unoType;
+ }
+
+ public MethodTypeInfo(String name, int index, int flags)
+ {
+ this(name, index, flags, null);
+ }
+
+ public int getIndex()
+ {
+ return m_index;
+ }
+
+ public boolean isReturnUnsigned()
+ {
+ return isUnsigned();
+ }
+
+ public boolean isOneway()
+ {
+ return (m_flags & TypeInfo.ONEWAY) != 0;
+ }
+
+ public boolean isConst()
+ {
+ return (m_flags & TypeInfo.CONST) != 0;
+ }
+
+ /**
+ Get the exact UNO return type of this method type info, in case it cannot
+ unambiguously be represented as a Java&nbsp;1.2 type.
+
+ @return the exact UNO return type of this method type info, or
+ <code>null</code> if the UNO type is already unambiguously represented by
+ the Java&nbsp;1.2 type
+
+ @since UDK 3.2
+ */
+ public final Type getUnoType() {
+ return m_unoType;
+ }
+}
+
+
diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java
new file mode 100644
index 000000000..f15ddc816
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.typeinfo;
+
+import com.sun.star.uno.Type;
+
+public class ParameterTypeInfo extends TypeInfo
+{
+ protected int m_index;
+ protected String m_methodName;
+ private final Type m_unoType; // @since UDK 3.2
+
+ /**
+ Create a parameter type info with a UNO type that cannot unambiguously be
+ represented as a Java&nbsp;1.2 type.
+
+ @param name the name of this parameter; must not be <code>null</code>
+
+ @param methodName the name of the method; must not be <code>null</code>
+
+ @param index the index among the parameters
+
+ @param flags any flags (<code>IN</code>, <code>OUT</code>,
+ <code>UNSIGNED</code>, <code>ANY</code>, <code>INTERFACE</code>)
+
+ @param unoType the exact UNO type; or <code>null</code> if the UNO type
+ is already unambiguously represented by the Java&nbsp;1.2 type
+
+ @since UDK 3.2
+ */
+ public ParameterTypeInfo(
+ String name, String methodName, int index, int flags, Type unoType)
+ {
+ super(name, flags);
+ m_index = index;
+ m_methodName = methodName;
+ m_unoType = unoType;
+ }
+
+ public ParameterTypeInfo(String name, String methodName, int index, int flags)
+ {
+ this(name, methodName, index, flags, null);
+ }
+
+ public String getMethodName()
+ {
+ return m_methodName;
+ }
+
+ public int getIndex()
+ {
+ return m_index;
+ }
+
+ public boolean isIN()
+ {
+ return ((m_flags & TypeInfo.IN) != 0 ||
+ (m_flags & (TypeInfo.IN | TypeInfo.OUT)) == 0); // nothing set => IN
+ }
+
+ public boolean isOUT()
+ {
+ return (m_flags & TypeInfo.OUT) != 0;
+ }
+
+ public boolean isINOUT()
+ {
+ return (m_flags & (TypeInfo.IN | TypeInfo.OUT)) == (TypeInfo.IN | TypeInfo.OUT);
+ }
+
+ /**
+ Get the exact UNO type of this parameter type info, in case it cannot
+ unambiguously be represented as a Java&nbsp;1.2 type.
+
+ <p>If this is an out or in&ndash;out parameter, the UNO type must be a
+ sequence type, taking into account that such a parameter is represented
+ in Java as a parameter of array type.</p>
+
+ @return the exact UNO type of this parameter type info, or
+ <code>null</code> if the UNO type is already unambiguously represented by
+ the Java&nbsp;1.2 type
+
+ @since UDK 3.2
+ */
+ public final Type getUnoType() {
+ return m_unoType;
+ }
+}
+
+
diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java
new file mode 100644
index 000000000..2503029fe
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.typeinfo;
+
+
+/** Defines a class to describe additional type information.
+ */
+public class TypeInfo
+{
+ public static final int IN = 0x00000001;
+ public static final int OUT = 0x00000002;
+ public static final int UNSIGNED = 0x00000004;
+ public static final int READONLY = 0x00000008;
+ public static final int ONEWAY = 0x00000010;
+ public static final int CONST = 0x00000020;
+ public static final int ANY = 0x00000040;
+ public static final int INTERFACE = 0x00000080;
+
+ /**
+ Marks an extended attribute of an interface type as bound.
+
+ <p>Only used in the <code>flags</code> argument of the
+ <code>AttributeTypeInfo</code> constructors.</p>
+
+ @since UDK 3.2
+ */
+ public static final int BOUND = 0x00000100;
+
+ protected int m_flags;
+ protected String m_name;
+
+ public TypeInfo(String name, int flags)
+ {
+ m_name = name;
+ m_flags = flags;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public int getFlags() {
+ return m_flags;
+ }
+
+ public boolean isUnsigned()
+ {
+ return (m_flags & TypeInfo.UNSIGNED) != 0;
+ }
+
+ public boolean isAny()
+ {
+ return (m_flags & TypeInfo.ANY) != 0;
+ }
+
+ public boolean isInterface()
+ {
+ return (m_flags & TypeInfo.INTERFACE) != 0;
+ }
+}
diff --git a/ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java b/ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java
new file mode 100644
index 000000000..588b8fe38
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java
@@ -0,0 +1,117 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.util;
+
+import java.util.LinkedList;
+
+/**
+ * Helper class to asynchronously execute finalize methods.
+ *
+ * <p>Current JVMs seem not to be robust against long-running finalize methods,
+ * in that such long-running finalize methods may lead to OutOfMemoryErrors.
+ * This class mitigates the problem by asynchronously shifting the bodies of
+ * potentially long-running finalize methods into an extra thread. Classes that
+ * make use of this in their finalize methods are the proxies used in the
+ * intra-process JNI UNO bridge and the inter-process Java URP UNO bridge (where
+ * in both cases finalizers lead to synchronous UNO release calls).</p>
+ *
+ * <p>Irrespective whether JVMs are getting more mature and should no longer
+ * have problems with long-running finalize methods, at least the JNI UNO bridge
+ * needs some way to stop finalization of proxies (to C++ objects) well before
+ * process exit, as provided by drain().</p>
+ */
+public final class AsynchronousFinalizer {
+ public AsynchronousFinalizer() {
+ thread = new Thread("AsynchronousFinalizer") {
+ @Override
+ public void run() {
+ for (;;) {
+ Job j;
+ synchronized (queue) {
+ for (;;) {
+ if (done) {
+ return;
+ }
+ if (!queue.isEmpty()) {
+ break;
+ }
+ try {
+ queue.wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ j = queue.remove(0);
+ }
+ try {
+ j.run();
+ } catch (Throwable e) {}
+ }
+ }
+ };
+ thread.start();
+ }
+
+ /**
+ * Add a job to be executed asynchronously.
+ *
+ * <p>The run method of the given job is called exactly once. If it terminates
+ * abnormally by throwing any Throwable, that is ignored.</p>
+ *
+ * @param job represents the body of some finalize method; must not be null.
+ */
+ public void add(Job job) {
+ synchronized (queue) {
+ boolean first = queue.isEmpty();
+ queue.add(job);
+ if (first) {
+ queue.notify();
+ }
+ }
+ }
+
+ public void drain() throws InterruptedException {
+ synchronized (queue) {
+ done = true;
+ queue.notify();
+ }
+ // tdf#123481 Only join if we are not in our own thread, else we have a deadlock
+ if (Thread.currentThread() != thread)
+ thread.join();
+ }
+
+ /**
+ * An interface to represent bodies of finalize methods.
+ *
+ * Similar to <code>Runnable</code>, except that the run method may throw any
+ * <code>Throwable</code> (which is effectively ignored by
+ * <code>AsynchronousFinalizer.add</code>, similar to any <code>Throwables</code>
+ * raised by finalize being ignored).
+ */
+ public interface Job {
+ void run() throws Throwable;
+ }
+
+ private final LinkedList<Job> queue = new LinkedList<Job>();
+ private final Thread thread;
+ private boolean done = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/util/DisposeListener.java b/ridljar/com/sun/star/lib/util/DisposeListener.java
new file mode 100644
index 000000000..6fdde2a8d
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/DisposeListener.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.util;
+
+/**
+ * Counterpart to <code>DisposeNotifier</code>.
+ *
+ * @see DisposeNotifier
+ */
+public interface DisposeListener {
+ /**
+ * Callback fired by a <code>DisposeNotifier</code> once it is disposed.
+ *
+ * @param source the <code>DisposeNotifier</code> that fires the callback;
+ * will never be <code>null</code>
+ */
+ void notifyDispose(DisposeNotifier source);
+}
diff --git a/ridljar/com/sun/star/lib/util/DisposeNotifier.java b/ridljar/com/sun/star/lib/util/DisposeNotifier.java
new file mode 100644
index 000000000..ebe0008d0
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/DisposeNotifier.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.util;
+
+/**
+ * Optional interface to be implemented by objects stored in a
+ * <code>WeakMap</code>.
+ *
+ * @see WeakMap
+ */
+public interface DisposeNotifier {
+ /**
+ * Adds a dispose listener, to be notified when this object is disposed.
+ *
+ * <p>It is unspecified what happens when the same listener is added
+ * multiple times.</p>
+ *
+ * <p>It is unspecified exactly when the <code>notifyDispose</code> callback
+ * is fired: immediately before the notifier is disposed, while it is in the
+ * process of disposing, or some time after it got disposed. But even if
+ * adding a listener to an already disposed notifier, the listener must
+ * eventually receive a <code>notifyDispose</code> callback.</p>
+ *
+ * @param listener a dispose listener, to be notified when this object is
+ * disposed; must not be <code>null</code>
+ */
+ void addDisposeListener(DisposeListener listener);
+}
diff --git a/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java b/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java
new file mode 100644
index 000000000..d255a71ea
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java
@@ -0,0 +1,141 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.util;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import com.sun.star.lib.unoloader.UnoClassLoader;
+
+/**
+ * Helper functions to locate and load native files.
+ *
+ * <p>The methods in this class are designed to find the requested resources in
+ * as many cases as possible. They search various places, roughly from most
+ * specific to most general. This works well if a component is known to bring
+ * with it a certain resource, and that resource has to be found. However, it
+ * might not work very well in cases where you want to check whether a
+ * component brings with it a certain resource or not: a similarly named
+ * resource from another component might be found by the eager search
+ * algorithm.</p>
+ */
+public final class NativeLibraryLoader {
+ static {
+ // if it's some other class loader, then resolving the symbol
+ // Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_dispatch_1call in
+ // java_uno will not work and result in UnsatisfiedLinkError
+ if (!(NativeLibraryLoader.class.getClassLoader() instanceof com.sun.star.lib.unoloader.UnoClassLoader)) {
+ System.err.println("warning: com.sun.star.lib.util.NativeLibraryLoader must be loaded by com.sun.star.lib.unoloader.UnoClassLoader for java_uno bridge to work (not a problem if this is using Java remote bridge or unit test)");
+ }
+ }
+ /**
+ * Load a system library, using a given class loader to locate the library.
+ *
+ * <p>This is similar to <code>System.loadLibrary</code>.</p>
+ *
+ * @param loader a class loader; may be null.
+ * @param libname the library name; how this name is mapped to a system
+ * library name is system dependent.
+ */
+ public static void loadLibrary(ClassLoader loader, String libname) {
+ String sysname = System.mapLibraryName(libname);
+ // At least Oracle's 1.7.0_51 now maps to .dylib rather than .jnilib:
+ if (System.getProperty("os.name").startsWith("Mac")
+ && sysname.endsWith(".dylib"))
+ {
+ sysname
+ = sysname.substring(0, sysname.length() - "dylib".length())
+ + "jnilib";
+ }
+ File path = getResource(loader, sysname);
+ if (path == null) {
+ // If the library cannot be found as a class loader resource, try
+ // the global System.loadLibrary as a last resort:
+ System.loadLibrary(libname);
+ } else {
+ System.load(path.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Locate a system resource, using a given class loader.
+ *
+ * <p>This is similar to <code>ClassLoader.getResource</code>, but only works
+ * for local resources (local files), and adds additional functionality for
+ * <code>URLClassLoaders</code>.</p>
+ *
+ * @param loader a class loader; may be null.
+ * @param name a resource name (that is, the name of a file).
+ * @return a File locating the resource, or null if the resource was not
+ * found.
+ */
+ public static File getResource(ClassLoader loader, String name) {
+ if (loader != null) {
+ File path = UrlToFileMapper.mapUrlToFile(loader.getResource(name));
+ if (path != null) {
+ return path;
+ }
+ }
+ // URLClassLoaders work on lists of URLs, which are typically URLs
+ // locating JAR files (scheme://auth/dir1/dir2/some.jar). The following
+ // code looks for resource name beside the JAR file
+ // (scheme://auth/dir1/dir2/name) and one directory up
+ // (scheme://auth/dir1/name). The second step is important in a typical
+ // OOo installation, where the JAR files are in the program/classes
+ // directory while the shared libraries are in the program directory.
+ if (!(loader instanceof URLClassLoader)) {
+ return null;
+ }
+ URL[] urls = ((URLClassLoader) loader).getURLs();
+ for (int i = 0; i < urls.length; ++i) {
+ File path = UrlToFileMapper.mapUrlToFile(urls[i]);
+ if (path != null) {
+ File dir = path.isDirectory() ? path : path.getParentFile();
+ if (dir != null) {
+ path = new File(dir, name);
+ if (path.exists()) {
+ return path;
+ }
+ dir = dir.getParentFile();
+ if (dir != null) {
+ path = new File(dir, name);
+ if (path.exists()) {
+ return path;
+ }
+ // On macOS, dir is now the Resources dir,
+ // we want to look in Frameworks
+ if (System.getProperty("os.name").startsWith("Mac")
+ && dir.getName().equals("Resources")) {
+ dir = dir.getParentFile();
+ path = new File(dir, "Frameworks/" + name);
+ if (path.exists()) {
+ return path;
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private NativeLibraryLoader() {} // do not instantiate
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/util/StringHelper.java b/ridljar/com/sun/star/lib/util/StringHelper.java
new file mode 100644
index 000000000..de8b5253a
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/StringHelper.java
@@ -0,0 +1,46 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.util;
+
+/**
+ * jurt.jar internal string helper methods.
+ */
+public final class StringHelper
+{
+ private StringHelper() {} // do not instantiate
+
+ public static String replace(String str, char from, String to) {
+ StringBuffer b = new StringBuffer();
+ for (int i = 0;;) {
+ int j = str.indexOf(from, i);
+ if (j == -1) {
+ b.append(str.substring(i));
+ break;
+ } else {
+ b.append(str.substring(i, j));
+ b.append(to);
+ i = j + 1;
+ }
+ }
+ return b.toString();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/util/UrlToFileMapper.java b/ridljar/com/sun/star/lib/util/UrlToFileMapper.java
new file mode 100644
index 000000000..131890f54
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/UrlToFileMapper.java
@@ -0,0 +1,94 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.util;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLEncoder;
+
+/**
+ * Maps Java URL representations to File representations, on any Java version.
+ *
+ * This used to be used to do URL to File mapping in pre-Java1.4 days, but since
+ * we now require Java 1.5, it is largely unnecessary.
+ *
+ * @since UDK 3.2.8
+ */
+public final class UrlToFileMapper {
+
+ /**
+ * Maps Java URL representations to File representations.
+ *
+ * @param url some URL, possibly null.
+ * @return a corresponding File, or null on failure.
+ */
+ public static File mapUrlToFile(URL url) {
+ if (url == null) {
+ return null;
+ } else {
+ try {
+ // where encodedUrl is url.toString(), but since that may contain
+ // unsafe characters (e.g., space, " "), it is encoded, as otherwise
+ // the URI constructor might throw java.net.URISyntaxException (in
+ // Java 1.5, URL.toURI might be used instead).
+ String encodedUrl = encode(url.toString());
+ URI uri = new URI(encodedUrl);
+ try {
+ return new File(uri);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ } catch (URISyntaxException ex) {
+ throw new RuntimeException(ex); // should never happen
+ }
+ }
+ }
+
+ private static String encode(String url) {
+ StringBuffer buf = new StringBuffer(url.length());
+ for (int i = 0; i < url.length(); ++i) {
+ char c = url.charAt(i);
+ // The RFC 2732 <uric> characters: !$&'()*+,-./:;=?@[]_~ plus digits
+ // and letters; additionally, do not encode % again.
+ if (c >= 'a' && c <= 'z' || c >= '?' && c <= '['
+ || c >= '$' && c <= ';' || c == '!' || c == '=' || c == ']'
+ || c == '_' || c == '~')
+ {
+ buf.append(c);
+ } else if (c == ' ') {
+ buf.append("%20");
+ } else {
+ try {
+ String enc = URLEncoder.encode(Character.toString(c), "UTF-8");
+ buf.append(enc);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e); // should never happen
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ private UrlToFileMapper() {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ridljar/com/sun/star/lib/util/WeakMap.java b/ridljar/com/sun/star/lib/util/WeakMap.java
new file mode 100644
index 000000000..f3b3b55a3
--- /dev/null
+++ b/ridljar/com/sun/star/lib/util/WeakMap.java
@@ -0,0 +1,319 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.lib.util;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A hash map that holds values of type <code>WeakReference</code>.
+ *
+ * <p>Like <code>HashMap</code>, this implementation provides all of the
+ * optional map operations, and permits the <code>null</code> key.</p>
+ *
+ * <p>Also like <code>HashMap</code>, this implementation is not synchronized.
+ * If multiple threads share an instance, and at least one of them executes any
+ * modifying operations on the <code>WeakMap</code>, they have to use external
+ * synchronization.</p>
+ *
+ * <p>Unlike other map implementations, <code>WeakMap</code> is asymmetric in
+ * that <code>put</code> expects the given value to be a plain object that is
+ * then wrapped in a <code>WeakReference</code>, while the occurrences of values
+ * in all other methods (<code>containsValue</code>, <code>entrySet</code>,
+ * <code>equals</code>, <code>get</code>, <code>hashCode</code>,
+ * <code>remove</code>, <code>values</code>, and also the return value of
+ * <code>put</code>) expect already wrapped instances of
+ * <code>WeakReference</code>. That is, after <code>weakMap.put("key",
+ * o)</code>, <code>weakMap.get("key").equals(o)</code> does not work as
+ * na&iuml;vely expected; neither does
+ * <code>weakMap1.putAll(weakMap2)</code>.</p>
+ *
+ * <p>At an arbitrary time after the <code>WeakReference</code> value of an
+ * entry has been cleared by the garbage collector, the entry is automatically
+ * removed from the map.</p>
+ *
+ * <p>Values placed into a <code>WeakMap</code> may optionally support the
+ * <code>DisposeNotifier</code> interface. For those that do, the associated
+ * <code>WeakReference</code> wrappers are automatically cleared as soon as the
+ * values are disposed.</p>
+ *
+ * Note that this class does not actually implement the Map interface properly,
+ * the type of the return value of the entrySet and values methods is wrong,
+ * but the "implements Map" is retained for backward compatibility.
+ */
+public final class WeakMap<K,V> implements Map {
+
+ /**
+ * Declare the map as WeakReference instead of Entry because it makes the return
+ * type signatures of values() and keySet() cleaner.
+ */
+ private final HashMap<K, WeakReference<V>> map = new HashMap<K, WeakReference<V>>();
+ private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
+
+ /**
+ * Constructs an empty <code>WeakMap</code>.
+ */
+ public WeakMap() {}
+
+ /**
+ * Constructs a new <code>WeakMap</code> with the same mappings as the
+ * specified <code>Map</code>.
+ *
+ * @param m the map whose mappings are to be placed in this map
+ */
+ public WeakMap(Map<K,V> m) {
+ putAll(m);
+ }
+
+ /**
+ * Returns the number of key&ndash;value mappings in this map.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @return the number of key&ndash;value mappings in this map
+ */
+ public int size() {
+ return map.size();
+ }
+
+ /**
+ * Returns <code>true</code> if this map contains no key&ndash;value
+ * mappings.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @return <code>true</code> if this map contains no key&ndash;value
+ * mappings
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * Returns <code>true</code> if this map contains a mapping for the
+ * specified key.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @param key the key whose presence in this map is to be tested
+ * @return <code>true</code> if this map contains a mapping for the
+ * specified key
+ */
+ public boolean containsKey(/*K*/ Object key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Returns <code>true</code> if this map maps one or more keys to the
+ * specified value.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @param value the value whose presence in this map is to be tested
+ * @return <code>true</code> if this map maps one or more keys to the
+ * specified value
+ */
+ public boolean containsValue(Object /*WeakReference<V>*/ value) {
+ return map.containsValue(value);
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped in this map, or
+ * <code>null</code> if the map contains no mapping for this key.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @param key the key whose associated value is to be returned
+ *
+ * @return the value to which this map maps the specified key, or
+ * <code>null</code> if the map contains no mapping for this key
+ */
+ public WeakReference<V> get(/*K*/ Object key) {
+ return map.get(key);
+ }
+
+ /**
+ * Associates the specified value with the specified key in this map.
+ *
+ * <p>This is a modifying operation.</p>
+ *
+ * @param key the key with which the specified value is to be associated
+ * @param value the value to be associated with the specified key. This
+ * must be a plain object, which is then wrapped in a
+ * <code>WeakReference</code>.
+ * @return previous value associated with the specified key, or
+ * <code>null</code> if there was no mapping for the key
+ */
+ public Object /*WeakReference<V>*/ put(/*K*/ Object key, /*V*/ Object value) {
+ cleanUp();
+ return map.put((K) key, new Entry<K,V>((K) key, (V) value, queue));
+ }
+
+ /**
+ * Removes the mapping for this key from this map if present.
+ *
+ * <p>This is a modifying operation.</p>
+ *
+ * @param key the key whose mapping is to be removed from the map
+ * @return previous value associated with the specified key, or
+ * <code>null</code> if there was no mapping for the key
+ */
+ public Object /*WeakReference<V>*/ remove(/*K*/ Object key) {
+ cleanUp();
+ return map.remove(key);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this map.
+ *
+ * <p>This is a modifying operation.</p>
+ *
+ * @param m mappings to be stored in this map. The values of those mappings
+ * must be plain objects, which are then wrapped in instances of
+ * <code>WeakReference</code>.
+ */
+ public void putAll(Map/*<K,V>*/ m) {
+ cleanUp();
+ for (Iterator<Map.Entry<K,V>> i = m.entrySet().iterator(); i.hasNext();) {
+ Map.Entry<K,V> e = i.next();
+ K k = e.getKey();
+ map.put(k, new Entry<K,V>(k, e.getValue(), queue));
+ }
+ }
+
+ /**
+ * Removes all mappings from this map.
+ *
+ * <p>This is a modifying operation.</p>
+ */
+ public void clear() {
+ cleanUp();
+ map.clear();
+ }
+
+ /**
+ * Returns a view of the keys contained in this map.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @return a set view of the keys contained in this map
+ */
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ /**
+ * Returns a collection view of the values contained in this map.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @return a collection view of the values contained in this map
+ */
+ public Collection<WeakReference<V>> values() {
+ return map.values();
+ }
+
+ /**
+ * Returns a collection view of the mappings contained in this map.
+ *
+ * <p>This is a non-modifying operation.</p>
+ *
+ * @return a collection view of the mappings contained in this map
+ */
+ public Set/*<Map.Entry<K,WeakReference<V>>>*/ entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return map.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ /**
+ * Returns the referent of a <code>WeakReference</code>, silently handling a
+ * <code>null</code> argument.
+ *
+ * <p>This static method is useful to wrap around the return values of
+ * methods like <code>get</code>.</p>
+ *
+ * @param ref must be either an instance of <code>WeakReference</code> or
+ * <code>null</code>
+ * @return the referent of the specified <code>WeakReference</code>, or
+ * <code>null</code> if <code>ref</code> is <code>null</code>
+ */
+ public static <T> T getValue(Object /*WeakReference<T>*/ ref) {
+ return ref == null ? null : ((WeakReference<T>) ref).get();
+ }
+
+ /**
+ * cleanUp() must only be called from within modifying methods. Otherwise,
+ * the implementations of entrySet, keySet and values would break
+ * (Specifically, iterating over the collections returned by those
+ * methods), as non-modifying methods might modify the underlying map.
+ **/
+ private void cleanUp() {
+ for (;;) {
+ Entry<K,V> e = (Entry<K,V>) queue.poll();
+ if (e == null) {
+ break;
+ }
+ // It is possible that an Entry e1 becomes weakly reachable, then
+ // another Entry e2 is added to the map for the same key, and only
+ // then e1 is enqueued. To not erroneously remove the new e2 in
+ // that case, check whether the map still contains e1:
+ Object k = e.key;
+ if (e == map.get(k)) {
+ map.remove(k);
+ }
+ }
+ }
+
+ private static final class Entry<K,V> extends WeakReference<V>
+ implements DisposeListener
+ {
+ private final K key;
+
+ private Entry(K key, V value, ReferenceQueue<V> queue) {
+ super(value, queue);
+ this.key = key;
+ if (value instanceof DisposeNotifier) {
+ ((DisposeNotifier) value).addDisposeListener(this);
+ }
+ }
+
+ /**
+ * @see DisposeListener#notifyDispose(DisposeNotifier)
+ */
+ public void notifyDispose(DisposeNotifier source) {
+ clear();
+ enqueue();
+ }
+ }
+
+}