From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- .../sun/star/comp/bridgefactory/BridgeFactory.java | 212 ++++ .../com/sun/star/comp/connections/Acceptor.java | 153 +++ .../com/sun/star/comp/connections/Connector.java | 130 +++ .../comp/connections/ConstantInstanceProvider.java | 118 +++ .../sun/star/comp/connections/Implementation.java | 95 ++ .../sun/star/comp/connections/PipedConnection.java | 259 +++++ ridljar/com/sun/star/comp/helper/Bootstrap.java | 492 +++++++++ .../sun/star/comp/helper/BootstrapException.java | 82 ++ .../com/sun/star/comp/helper/ComponentContext.java | 308 ++++++ .../star/comp/helper/ComponentContextEntry.java | 64 ++ .../sun/star/comp/helper/SharedLibraryLoader.java | 175 +++ .../com/sun/star/comp/loader/FactoryHelper.java | 502 +++++++++ ridljar/com/sun/star/comp/loader/JavaLoader.java | 439 ++++++++ .../sun/star/comp/loader/JavaLoaderFactory.java | 90 ++ .../star/comp/loader/RegistrationClassFinder.java | 69 ++ .../star/comp/servicemanager/ServiceManager.java | 662 ++++++++++++ .../com/sun/star/comp/urlresolver/UrlResolver.java | 147 +++ .../star/lib/connections/pipe/PipeConnection.java | 213 ++++ .../star/lib/connections/pipe/pipeAcceptor.java | 123 +++ .../star/lib/connections/pipe/pipeConnector.java | 120 +++ .../connections/socket/ConnectionDescriptor.java | 98 ++ .../lib/connections/socket/SocketConnection.java | 235 +++++ .../lib/connections/socket/socketAcceptor.java | 202 ++++ .../lib/connections/socket/socketConnector.java | 174 +++ .../websocket/ConnectionDescriptor.java | 60 ++ .../connections/websocket/WebsocketConnection.java | 326 ++++++ .../connections/websocket/websocketConnector.java | 137 +++ ridljar/com/sun/star/lib/uno/Proxy.java | 34 + .../adapter/ByteArrayToXInputStreamAdapter.java | 117 +++ .../adapter/InputStreamToXInputStreamAdapter.java | 157 +++ .../OutputStreamToXOutputStreamAdapter.java | 80 ++ .../adapter/XInputStreamToInputStreamAdapter.java | 218 ++++ .../adapter/XOutputStreamToByteArrayAdapter.java | 94 ++ .../XOutputStreamToOutputStreamAdapter.java | 117 +++ .../lib/uno/bridges/java_remote/BridgedObject.java | 43 + .../lib/uno/bridges/java_remote/ProxyFactory.java | 198 ++++ .../uno/bridges/java_remote/RequestHandler.java | 35 + .../XConnectionInputStream_Adapter.java | 76 ++ .../XConnectionOutputStream_Adapter.java | 89 ++ .../bridges/java_remote/java_remote_bridge.java | 700 ++++++++++++ .../uno/environments/java/java_environment.java | 312 ++++++ .../lib/uno/environments/remote/IProtocol.java | 93 ++ .../lib/uno/environments/remote/IReceiver.java | 40 + .../lib/uno/environments/remote/IThreadPool.java | 115 ++ .../uno/environments/remote/JavaThreadPool.java | 124 +++ .../environments/remote/JavaThreadPoolFactory.java | 87 ++ .../sun/star/lib/uno/environments/remote/Job.java | 163 +++ .../star/lib/uno/environments/remote/JobQueue.java | 373 +++++++ .../star/lib/uno/environments/remote/Message.java | 189 ++++ .../uno/environments/remote/NativeThreadPool.java | 94 ++ .../star/lib/uno/environments/remote/ThreadId.java | 109 ++ .../uno/environments/remote/ThreadPoolManager.java | 74 ++ .../environments/remote/remote_environment.java | 66 ++ .../com/sun/star/lib/uno/helper/ComponentBase.java | 136 +++ ridljar/com/sun/star/lib/uno/helper/Factory.java | 291 +++++ .../star/lib/uno/helper/InterfaceContainer.java | 864 +++++++++++++++ .../uno/helper/MultiTypeInterfaceContainer.java | 155 +++ .../com/sun/star/lib/uno/helper/PropertySet.java | 1103 +++++++++++++++++++ .../sun/star/lib/uno/helper/PropertySetMixin.java | 1111 ++++++++++++++++++++ ridljar/com/sun/star/lib/uno/helper/UnoUrl.java | 401 +++++++ .../com/sun/star/lib/uno/helper/WeakAdapter.java | 94 ++ ridljar/com/sun/star/lib/uno/helper/WeakBase.java | 101 ++ .../com/sun/star/lib/uno/protocols/urp/Cache.java | 114 ++ .../sun/star/lib/uno/protocols/urp/Marshal.java | 355 +++++++ .../lib/uno/protocols/urp/PendingRequests.java | 65 ++ .../sun/star/lib/uno/protocols/urp/Unmarshal.java | 476 +++++++++ .../sun/star/lib/uno/protocols/urp/UrpMessage.java | 48 + .../com/sun/star/lib/uno/protocols/urp/urp.java | 760 +++++++++++++ .../star/lib/uno/typedesc/FieldDescription.java | 78 ++ .../lib/uno/typedesc/MemberDescriptionHelper.java | 54 + .../star/lib/uno/typedesc/MethodDescription.java | 131 +++ .../sun/star/lib/uno/typedesc/TypeDescription.java | 819 +++++++++++++++ .../star/lib/uno/typeinfo/AttributeTypeInfo.java | 86 ++ .../star/lib/uno/typeinfo/ConstantTypeInfo.java | 32 + .../sun/star/lib/uno/typeinfo/MemberTypeInfo.java | 96 ++ .../sun/star/lib/uno/typeinfo/MethodTypeInfo.java | 89 ++ .../star/lib/uno/typeinfo/ParameterTypeInfo.java | 105 ++ .../com/sun/star/lib/uno/typeinfo/TypeInfo.java | 76 ++ .../sun/star/lib/util/AsynchronousFinalizer.java | 117 +++ ridljar/com/sun/star/lib/util/DisposeListener.java | 34 + ridljar/com/sun/star/lib/util/DisposeNotifier.java | 44 + .../com/sun/star/lib/util/NativeLibraryLoader.java | 132 +++ ridljar/com/sun/star/lib/util/StringHelper.java | 46 + ridljar/com/sun/star/lib/util/UrlToFileMapper.java | 94 ++ ridljar/com/sun/star/lib/util/WeakMap.java | 323 ++++++ ridljar/com/sun/star/uno/Any.java | 151 +++ ridljar/com/sun/star/uno/AnyConverter.java | 668 ++++++++++++ ridljar/com/sun/star/uno/Ascii.java | 43 + ridljar/com/sun/star/uno/AsciiString.java | 44 + ridljar/com/sun/star/uno/Enum.java | 50 + ridljar/com/sun/star/uno/IBridge.java | 94 ++ ridljar/com/sun/star/uno/IEnvironment.java | 144 +++ ridljar/com/sun/star/uno/IMapping.java | 41 + ridljar/com/sun/star/uno/IQueryInterface.java | 60 ++ ridljar/com/sun/star/uno/MappingException.java | 65 ++ ridljar/com/sun/star/uno/Type.java | 699 ++++++++++++ ridljar/com/sun/star/uno/UnoRuntime.java | 726 +++++++++++++ ridljar/com/sun/star/uno/WeakReference.java | 154 +++ 98 files changed, 20851 insertions(+) create mode 100644 ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java create mode 100644 ridljar/com/sun/star/comp/connections/Acceptor.java create mode 100644 ridljar/com/sun/star/comp/connections/Connector.java create mode 100644 ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java create mode 100644 ridljar/com/sun/star/comp/connections/Implementation.java create mode 100644 ridljar/com/sun/star/comp/connections/PipedConnection.java create mode 100644 ridljar/com/sun/star/comp/helper/Bootstrap.java create mode 100644 ridljar/com/sun/star/comp/helper/BootstrapException.java create mode 100644 ridljar/com/sun/star/comp/helper/ComponentContext.java create mode 100644 ridljar/com/sun/star/comp/helper/ComponentContextEntry.java create mode 100644 ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java create mode 100644 ridljar/com/sun/star/comp/loader/FactoryHelper.java create mode 100644 ridljar/com/sun/star/comp/loader/JavaLoader.java create mode 100644 ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java create mode 100644 ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java create mode 100644 ridljar/com/sun/star/comp/servicemanager/ServiceManager.java create mode 100644 ridljar/com/sun/star/comp/urlresolver/UrlResolver.java create mode 100644 ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java create mode 100644 ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java create mode 100644 ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java create mode 100644 ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java create mode 100644 ridljar/com/sun/star/lib/connections/socket/SocketConnection.java create mode 100644 ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java create mode 100644 ridljar/com/sun/star/lib/connections/socket/socketConnector.java create mode 100644 ridljar/com/sun/star/lib/connections/websocket/ConnectionDescriptor.java create mode 100644 ridljar/com/sun/star/lib/connections/websocket/WebsocketConnection.java create mode 100644 ridljar/com/sun/star/lib/connections/websocket/websocketConnector.java create mode 100644 ridljar/com/sun/star/lib/uno/Proxy.java create mode 100644 ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java create mode 100644 ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java create mode 100644 ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java create mode 100644 ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java create mode 100644 ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java create mode 100644 ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/java/java_environment.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/Job.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/Message.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java create mode 100644 ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/ComponentBase.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/Factory.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/PropertySet.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/UnoUrl.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java create mode 100644 ridljar/com/sun/star/lib/uno/helper/WeakBase.java create mode 100644 ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java create mode 100644 ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java create mode 100644 ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java create mode 100644 ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java create mode 100644 ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java create mode 100644 ridljar/com/sun/star/lib/uno/protocols/urp/urp.java create mode 100644 ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java create mode 100644 ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java create mode 100644 ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java create mode 100644 ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java create mode 100644 ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java create mode 100644 ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java create mode 100644 ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java create mode 100644 ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java create mode 100644 ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java create mode 100644 ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java create mode 100644 ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java create mode 100644 ridljar/com/sun/star/lib/util/DisposeListener.java create mode 100644 ridljar/com/sun/star/lib/util/DisposeNotifier.java create mode 100644 ridljar/com/sun/star/lib/util/NativeLibraryLoader.java create mode 100644 ridljar/com/sun/star/lib/util/StringHelper.java create mode 100644 ridljar/com/sun/star/lib/util/UrlToFileMapper.java create mode 100644 ridljar/com/sun/star/lib/util/WeakMap.java create mode 100644 ridljar/com/sun/star/uno/Any.java create mode 100644 ridljar/com/sun/star/uno/AnyConverter.java create mode 100644 ridljar/com/sun/star/uno/Ascii.java create mode 100644 ridljar/com/sun/star/uno/AsciiString.java create mode 100644 ridljar/com/sun/star/uno/Enum.java create mode 100644 ridljar/com/sun/star/uno/IBridge.java create mode 100644 ridljar/com/sun/star/uno/IEnvironment.java create mode 100644 ridljar/com/sun/star/uno/IMapping.java create mode 100644 ridljar/com/sun/star/uno/IQueryInterface.java create mode 100644 ridljar/com/sun/star/uno/MappingException.java create mode 100644 ridljar/com/sun/star/uno/Type.java create mode 100644 ridljar/com/sun/star/uno/UnoRuntime.java create mode 100644 ridljar/com/sun/star/uno/WeakReference.java (limited to 'ridljar/com/sun/star') diff --git a/ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java b/ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java new file mode 100644 index 0000000000..031e60d3f1 --- /dev/null +++ b/ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java @@ -0,0 +1,212 @@ +/* -*- 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.comp.bridgefactory; + +import java.math.BigInteger; +import java.util.ArrayList; + +import com.sun.star.bridge.BridgeExistsException; +import com.sun.star.bridge.XBridge; +import com.sun.star.bridge.XBridgeFactory; +import com.sun.star.bridge.XInstanceProvider; +import com.sun.star.comp.loader.FactoryHelper; +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 com.sun.star.uno.IBridge; +import com.sun.star.uno.UnoRuntime; + + +/** + * The BridgeFactory class implements the XBridgeFactory Interface. + * + *

It wraps the UnoRuntime#getBridgeByNamemethod and delivers a + * XBridge component.

+ * + *

This component is only usable for remote bridges.

+ * + * @see com.sun.star.uno.UnoRuntime + * @since UDK1.0 + */ +public class BridgeFactory implements XBridgeFactory/*, XEventListener*/ { + private static final boolean DEBUG = false; + + /** + * The name of the service, the JavaLoader accesses this through + * reflection. + */ + public static final String __serviceName = "com.sun.star.bridge.BridgeFactory"; + + /** + * Gives a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(BridgeFactory.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(BridgeFactory.class, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + /** + * Creates a remote bridge and memorizes it under sName. + * + * @param sName the name to memorize the bridge. + * @param sProtocol the protocol the bridge should use. + * @param anInstanceProvider the instance provider. + * @return the bridge. + * + * @see com.sun.star.bridge.XBridgeFactory + */ + public XBridge createBridge(String sName, String sProtocol, XConnection aConnection, XInstanceProvider anInstanceProvider) throws + BridgeExistsException, + com.sun.star.lang.IllegalArgumentException, + com.sun.star.uno.RuntimeException + { + boolean hasName = sName.length() != 0; + Object context = hasName ? sName : new UniqueToken(); + // UnoRuntime.getBridgeByName internally uses context.toString() to + // distinguish bridges, so the result of + // new UniqueToken().toString() might clash with an explicit + // sName.toString(), but the UnoRuntime bridge management is + // obsolete anyway and should be removed + + // do not create a new bridge, if one already exists + if (hasName) { + IBridge iBridges[] = UnoRuntime.getBridges(); + for(int i = 0; i < iBridges.length; ++ i) { + XBridge xBridge = UnoRuntime.queryInterface(XBridge.class, iBridges[i]); + + if(xBridge != null && xBridge.getName().equals(sName)) { + throw new BridgeExistsException(sName + " already exists"); + } + } + } + + XBridge xBridge; + + try { + IBridge iBridge = UnoRuntime.getBridgeByName("java", context, "remote", context, hasName ? new Object[]{sProtocol, aConnection, anInstanceProvider, sName} : new Object[]{sProtocol, aConnection, anInstanceProvider}); + + xBridge = UnoRuntime.queryInterface(XBridge.class, iBridge); + } + catch (Exception e) { + throw new com.sun.star.lang.IllegalArgumentException(e, e.getMessage()); + } + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".createBridge:" + sName + " " + sProtocol + " " + aConnection + " " + anInstanceProvider + " " + xBridge); + + return xBridge; + } + + /** + * Gets a remote bridge which must already exist. + * + * @param sName the name of the bridge. + * @return the bridge. + * @see com.sun.star.bridge.XBridgeFactory + */ + public XBridge getBridge(String sName) throws com.sun.star.uno.RuntimeException { + XBridge xBridge = null; + + IBridge iBridges[] = UnoRuntime.getBridges(); + for(int i = 0; i < iBridges.length; ++ i) { + xBridge = UnoRuntime.queryInterface(XBridge.class, iBridges[i]); + + if(xBridge != null) { + if(xBridge.getName().equals(sName)) + break; + + else + xBridge = null; + } + } + + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".getBridge:" + sName + " " + xBridge); + + return xBridge; + } + + /** + * Gives all created bridges. + * + * @return the bridges. + * @see com.sun.star.bridge.XBridgeFactory + */ + public synchronized XBridge[] getExistingBridges() throws com.sun.star.uno.RuntimeException { + ArrayList vector = new ArrayList(); + + IBridge iBridges[] = UnoRuntime.getBridges(); + for(int i = 0; i < iBridges.length; ++ i) { + XBridge xBridge = UnoRuntime.queryInterface(XBridge.class, iBridges[i]); + + if(xBridge != null) + vector.add(xBridge); + } + + XBridge xBridges[]= new XBridge[vector.size()]; + for(int i = 0; i < vector.size(); ++ i) + xBridges[i] = vector.get(i); + + return xBridges; + } + + private static final class UniqueToken { + public UniqueToken() { + synchronized (UniqueToken.class) { + token = counter.toString(); + counter = counter.add(BigInteger.ONE); + } + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return token; + } + + private final String token; + private static BigInteger counter = BigInteger.ZERO; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/Acceptor.java b/ridljar/com/sun/star/comp/connections/Acceptor.java new file mode 100644 index 0000000000..996eaeb858 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/Acceptor.java @@ -0,0 +1,153 @@ +/* -*- 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.comp.connections; + +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 XAcceptor interface. + * + *

The Acceptor is a general component, that uses less general + * components (like com.sun.star.connection.socketAcceptor) to + * implement its functionality.

+ * + * @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 Acceptor implements XAcceptor { + /** + * The name of the service. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.Acceptor"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(Acceptor.class.getName()) + ? FactoryHelper.getServiceFactory(Acceptor.class, __serviceName, + multiFactory, regKey) + : null; + } + + /** + * Constructs a new Acceptor that uses the given service + * factory to create a specific XAcceptor. + * + * @param serviceFactory the service factory to use. + */ + public Acceptor(XMultiServiceFactory serviceFactory) { + this.serviceFactory = serviceFactory; + } + + /** + * Accepts a connection request via the given connection type. + * + *

This call blocks until a connection has been established.

+ * + *

The connection description has the following format: + * type*(key=value). + * The specific XAcceptor implementation is instantiated + * through the service factory as + * com.sun.star.connection.typeAcceptor (with + * type in lower case).

+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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 + { + if (DEBUG) { + System.err.println("##### " + getClass().getName() + ".accept(" + + connectionDescription + ")"); + } + XAcceptor acc; + synchronized (this) { + if (acceptor == null) { + acceptor = (XAcceptor) Implementation.getConnectionService( + serviceFactory, connectionDescription, XAcceptor.class, + "Acceptor"); + acceptingDescription = connectionDescription; + } else if (!connectionDescription.equals(acceptingDescription)) { + throw new AlreadyAcceptingException(acceptingDescription + + " vs. " + + connectionDescription); + } + acc = acceptor; + } + return acc.accept(connectionDescription); + } + + /** + * + * @see com.sun.star.connection.XAcceptor#stopAccepting + */ + public void stopAccepting() { + XAcceptor acc; + synchronized (this) { + acc = acceptor; + } + acc.stopAccepting(); + } + + private static final boolean DEBUG = false; + + private final XMultiServiceFactory serviceFactory; + + private XAcceptor acceptor = null; + private String acceptingDescription; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/ridljar/com/sun/star/comp/connections/Connector.java b/ridljar/com/sun/star/comp/connections/Connector.java new file mode 100644 index 0000000000..6ffab9eea8 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/Connector.java @@ -0,0 +1,130 @@ +/* -*- 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.comp.connections; + +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 XConnector interface. + * + *

The Connector is a general component, that uses less general + * components (like com.sun.star.connection.socketConnector) to + * implement its functionality.

+ * + * @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 class Connector implements XConnector { + /** + * The name of the service. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.Connector"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(Connector.class.getName()) + ? FactoryHelper.getServiceFactory(Connector.class, __serviceName, + multiFactory, regKey) + : null; + } + + /** + * Constructs a new Connector that uses the given service + * factory to create a specific XConnector. + * + * @param serviceFactory the service factory to use. + */ + public Connector(XMultiServiceFactory serviceFactory) { + this.serviceFactory = serviceFactory; + } + + /** + * Connects via the given connection type to a waiting server. + * + *

The connection description has the following format: + * type*(key=value). + * The specific XConnector implementation is instantiated + * through the service factory as + * com.sun.star.connection.typeConnector (with + * type in lower case).

+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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 (DEBUG) { + System.err.println("##### " + getClass().getName() + ".connect(" + + connectionDescription + ")"); + } + if (connected) { + throw new ConnectionSetupException("already connected"); + } + XConnection con + = ((XConnector) Implementation.getConnectionService( + serviceFactory, connectionDescription, XConnector.class, + "Connector")).connect(connectionDescription); + connected = true; + return con; + } + + private static final boolean DEBUG = false; + + private final XMultiServiceFactory serviceFactory; + + private boolean connected = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java b/ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java new file mode 100644 index 0000000000..6c2fcb2d85 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java @@ -0,0 +1,118 @@ +/* -*- 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.comp.connections; + +import com.sun.star.bridge.XInstanceProvider; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.comp.loader.FactoryHelper; + + +/** + * The ConstantInstanceProvider is a component + * that implements the XInstanceProvider Interface. + * + * @see com.sun.star.bridge.XBridge + * @see com.sun.star.bridge.XBridgeFactory + * @see com.sun.star.bridge.XInstanceProvider + * @see com.sun.star.comp.loader.JavaLoader + * @since UDK1.0 + */ +public class ConstantInstanceProvider implements XInstanceProvider { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + + /** + * The name of the service, the JavaLoader accesses this through + * reflection. + */ + private static final String __serviceName = "com.sun.star.comp.connection.InstanceProvider"; + + /** + * Gives a factory for creating the service. + *

This method is called by the JavaLoader.

+ * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(ConstantInstanceProvider.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(ConstantInstanceProvider.class, + __serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + protected XMultiServiceFactory _serviceManager; + protected String _serviceName; + protected Object _instance; + + + public void setInstance(String serviceName) throws com.sun.star.uno.Exception { + _instance = _serviceManager.createInstance(serviceName); + _serviceName = serviceName; + } + + /** + * Constructs a new ConstantInstanceProvider. + *

Uses the provided ServiceManager as the provided instance.

+ * + * @param serviceManager the provided service manager + */ + public ConstantInstanceProvider(XMultiServiceFactory serviceManager) { + _serviceManager = serviceManager; + + _serviceName = "SERVICEMANAGER"; + _instance = serviceManager; + } + + /** + * Gives an object for the passed instance name. + * + * @return the desired instance + * @param sInstanceName the name of the desired instance + */ + public Object getInstance(String sInstanceName) throws com.sun.star.container.NoSuchElementException, com.sun.star.uno.RuntimeException { + Object result = sInstanceName.equals(_serviceName) ? _instance : null; + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".getInstance(" + sInstanceName + "):" + result); + + return result; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/Implementation.java b/ridljar/com/sun/star/comp/connections/Implementation.java new file mode 100644 index 0000000000..94cabe253a --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/Implementation.java @@ -0,0 +1,95 @@ +/* -*- 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.comp.connections; + +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; + +/** + * Helper class for Acceptor and Connector. + */ +final class Implementation { + /** + * Instantiate a service for a given connection type. + * + * @param factory the service factory used to instantiate the requested + * service. + * @param description has the following format: + * type*(key=value). + * The specific service implementation is instantiated through the + * service factory as + * com.sun.star.connection.typeservice + * (with type in lower case, and + * service either Acceptor or + * Connector). + * @param serviceClass the IDL interface type for which to query the + * requested service. + * @param serviceType must be either Acceptor or + * Connector. + * @return an instance of the requested service. Never returns + * null. + * @throws ConnectionSetupException if the requested service can not be + * found, or cannot be instantiated. + */ + public static Object getConnectionService(XMultiServiceFactory factory, + String description, + Class serviceClass, + String serviceType) + throws ConnectionSetupException + { + int i = description.indexOf(','); + String type + = (i < 0 ? description : description.substring(0, i)).toLowerCase(); + Object service = null; + try { + service = UnoRuntime.queryInterface( + serviceClass, + factory.createInstance("com.sun.star.connection." + type + + serviceType)); + } catch (RuntimeException e) { + throw e; + } catch (com.sun.star.uno.Exception e) { + } + if (service == null) { + // As a fallback, also try to instantiate the service from the + // com.sun.star.lib.connections package structure: + try { + service + = Class.forName("com.sun.star.lib.connections." + type + + "." + type + serviceType).newInstance(); + } catch (ClassNotFoundException e) { + } catch (IllegalAccessException e) { + } catch (InstantiationException e) { + } + } + if (service == null) { + throw new ConnectionSetupException("no " + serviceType + " for " + + type); + } + return service; + } + + private Implementation() {} // do not instantiate +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/PipedConnection.java b/ridljar/com/sun/star/comp/connections/PipedConnection.java new file mode 100644 index 0000000000..631cfae40e --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/PipedConnection.java @@ -0,0 +1,259 @@ +/* -*- 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.comp.connections; + + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * The PipedConnection is a component that implements the + * XConnection Interface. + *

It is useful for Thread communication in one Process.

+ * + * @see com.sun.star.connection.XConnection + * @see com.sun.star.comp.loader.JavaLoader + * @since UDK1.0 + */ +public class PipedConnection implements XConnection { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + + /** + * The name of the service, the JavaLoader accesses this through + * reflection. + */ + private static final String __serviceName = "com.sun.star.connection.PipedConnection"; + + /** + * Gives a factory for creating the service. + *

This method is called by the JavaLoader.

+ * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(PipedConnection.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(PipedConnection.class, + __serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + /** + * The amount of time in milliseconds, to wait to see check the buffers. + */ + protected static final int __waitTime = 10000; + + protected byte _buffer[] = new byte[4096]; + protected int _in, + _out; + protected boolean _closed; + protected PipedConnection _otherSide; + + /** + * Constructs a new PipedConnection, sees if there is another + * side, which it should be connected to. + * + * @param args Another side could be in index 0. + */ + public PipedConnection(Object args[]) throws com.sun.star.uno.RuntimeException { + if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated"); + + _otherSide = (args.length == 1) ? (PipedConnection)args[0] : null; + if(_otherSide != null) { + if(_otherSide == this) + throw new RuntimeException("can not connect to myself"); + + _otherSide._otherSide = this; + } + } + + /** + * This is a private method, used to communicate internal in the pipe. + */ + private synchronized void receive(byte aData[]) throws com.sun.star.io.IOException { + int bytesWritten = 0; + + if(DEBUG) System.err.println("##### PipedConnection.receive - bytes:" + aData.length + " at:" + _out); + + while(bytesWritten < aData.length) { + // wait until it is not full anymore + while(_out == (_in - 1) || (_in == 0 && _out == _buffer.length - 1)) { + try { + notify(); // the buffer is full, signal it + + wait(__waitTime); + } + catch(InterruptedException interruptedException) { + throw new com.sun.star.io.IOException(interruptedException); + } + } + + if(_closed) throw new com.sun.star.io.IOException("connection has been closed"); + + int bytes ; + + if(_out < _in) { + bytes = Math.min(aData.length - bytesWritten, _in - _out - 1); + + System.arraycopy(aData, bytesWritten, _buffer, _out, bytes); + } + else { + if(_in > 0){ + bytes = Math.min(aData.length - bytesWritten, _buffer.length - _out); + } + else { + bytes = Math.min(aData.length - bytesWritten, _buffer.length - _out - 1); + } + + System.arraycopy(aData, bytesWritten, _buffer, _out, bytes); + } + + bytesWritten += bytes; + _out += bytes; + if(_out >= _buffer.length) + _out = 0; + } + } + + /** + * Read the required number of bytes. + * + * @param aReadBytes the out parameter, 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 synchronized int read(/*OUT*/byte[][] aReadBytes, int nBytesToRead) throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + aReadBytes[0] = new byte[nBytesToRead]; + + if(DEBUG) System.err.println("##### PipedConnection.read - bytes:" + nBytesToRead + " at:" + _in); + + // loop while not all bytes read or when closed but there is still data + while(nBytesToRead > 0 && (_in != _out || !_closed)) { + while(_in == _out && !_closed) { + try { + notify(); // the buffer is empty, signal it + + wait(__waitTime); // we wait for data or for the pipe to be closed + } + catch(InterruptedException interruptedException) { + throw new com.sun.star.io.IOException(interruptedException); + } + } + + if(_in < _out) { + int bytes = Math.min(nBytesToRead, _out - _in); + + System.arraycopy(_buffer, _in, aReadBytes[0], aReadBytes[0].length - nBytesToRead, bytes); + + nBytesToRead -= bytes; + _in += bytes; + } + else if(_in > _out) { + int bytes = Math.min(nBytesToRead, _buffer.length - _in); + + System.arraycopy(_buffer, _in, aReadBytes[0], aReadBytes[0].length - nBytesToRead, bytes); + + nBytesToRead -= bytes; + _in += bytes; + if(_in >= _buffer.length) + _in = 0; + } + } + + if(nBytesToRead > 0) { // not all bytes read + byte tmp[] = new byte[aReadBytes[0].length - nBytesToRead]; + System.arraycopy(aReadBytes[0], 0, tmp, 0, tmp.length); + + aReadBytes[0] = tmp; + } + + return aReadBytes[0].length; + } + + /** + * 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 { + _otherSide.receive(aData); + } + + /** + * Flushes the buffer, notifies if necessary the other side that new data has + * arrived. + * + * @see com.sun.star.connection.XConnection#flush + */ + public void flush() throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + synchronized(_otherSide) { + _otherSide.notify(); + } + } + + /** + * Closes the pipe. + * + * @see com.sun.star.connection.XConnection#close() + */ + public synchronized void close() throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + if(!_closed) { + _closed = true; + + _otherSide.close(); + + notify(); + } + } + + /** + * Gives a description of this pipe. + * + * @return the description. + * @see com.sun.star.connection.XConnection#getDescription + */ + public String getDescription() throws com.sun.star.uno.RuntimeException { + return getClass().getName(); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/helper/Bootstrap.java b/ridljar/com/sun/star/comp/helper/Bootstrap.java new file mode 100644 index 0000000000..6b371f5032 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/Bootstrap.java @@ -0,0 +1,492 @@ +// -*- 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.comp.helper; + +import com.sun.star.bridge.UnoUrlResolver; +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.comp.loader.JavaLoader; +import com.sun.star.comp.servicemanager.ServiceManager; +import com.sun.star.container.XSet; +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lib.util.NativeLibraryLoader; +import com.sun.star.loader.XImplementationLoader; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.beans.XPropertySet; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Random; + +/** Bootstrap offers functionality to obtain a context or simply + a service manager. + The service manager can create a few basic services, whose implementations are: +
    +
  • com.sun.star.comp.loader.JavaLoader
  • +
  • com.sun.star.comp.urlresolver.UrlResolver
  • +
  • com.sun.star.comp.bridgefactory.BridgeFactory
  • +
  • com.sun.star.comp.connections.Connector
  • +
  • com.sun.star.comp.connections.Acceptor
  • +
  • com.sun.star.comp.servicemanager.ServiceManager
  • +
+ + Other services can be inserted into the service manager by + using its XSet interface: +
+        XSet xSet = UnoRuntime.queryInterface( XSet.class, aMultiComponentFactory );
+        // insert the service manager
+        xSet.insert( aSingleComponentFactory );
+    
+*/ +public class Bootstrap { + + private static void insertBasicFactories( + XSet xSet, XImplementationLoader xImpLoader ) + throws Exception + { + // insert the factory of the loader + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.loader.JavaLoader", null, null, null ) ); + + // insert the factory of the URLResolver + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.urlresolver.UrlResolver", null, null, null ) ); + + // insert the bridgefactory + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.bridgefactory.BridgeFactory", null, null, null ) ); + + // insert the connector + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.connections.Connector", null, null, null ) ); + + // insert the acceptor + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.connections.Acceptor", null, null, null ) ); + } + + /** + * Returns an array of default commandline options to start bootstrapped + * instance of soffice with. You may use it in connection with bootstrap + * method for example like this: + *
+     *     List list = Arrays.asList( Bootstrap.getDefaultOptions() );
+     *     list.remove("--nologo");
+     *     list.remove("--nodefault");
+     *     list.add("--invisible");
+     *
+     *     Bootstrap.bootstrap( list.toArray( new String[list.size()] );
+     * 
+ * + * @return an array of default commandline options + * @see #bootstrap( String[] ) + * @since LibreOffice 5.1 + */ + public static final String[] getDefaultOptions() + { + return new String[] + { + "--nologo", + "--nodefault", + "--norestore", + "--nolockcheck" + }; + } + + /** + backwards compatibility stub. + @param context_entries the hash table contains mappings of entry names (type string) to + context entries (type class ComponentContextEntry). + @throws Exception if things go awry. + @return a new context. + */ + public static XComponentContext createInitialComponentContext( Hashtable context_entries ) + throws Exception + { + return createInitialComponentContext((Map) context_entries); + } + /** Bootstraps an initial component context with service manager and basic + jurt components inserted. + @param context_entries the hash table contains mappings of entry names (type string) to + context entries (type class ComponentContextEntry). + @throws Exception if things go awry. + @return a new context. + */ + public static XComponentContext createInitialComponentContext( Map context_entries ) + throws Exception + { + ServiceManager xSMgr = new ServiceManager(); + + XImplementationLoader xImpLoader = UnoRuntime.queryInterface( + XImplementationLoader.class, new JavaLoader() ); + XInitialization xInit = UnoRuntime.queryInterface( + XInitialization.class, xImpLoader ); + Object[] args = new Object [] { xSMgr }; + xInit.initialize( args ); + + // initial component context + if (context_entries == null) + context_entries = new HashMap( 1 ); + // add smgr + context_entries.put( + "/singletons/com.sun.star.lang.theServiceManager", + new ComponentContextEntry( null, xSMgr ) ); + // ... xxx todo: add standard entries + XComponentContext xContext = new ComponentContext( context_entries, null ); + + xSMgr.setDefaultContext(xContext); + + XSet xSet = UnoRuntime.queryInterface( XSet.class, xSMgr ); + // insert basic jurt factories + insertBasicFactories( xSet, xImpLoader ); + + return xContext; + } + + /** + * Bootstraps a servicemanager with the jurt base components registered. + * + * See also UNOIDL com.sun.star.lang.ServiceManager. + * + * @throws Exception if things go awry. + * @return a freshly bootstrapped service manager + */ + public static XMultiServiceFactory createSimpleServiceManager() throws Exception + { + return UnoRuntime.queryInterface( + XMultiServiceFactory.class, createInitialComponentContext( (Map) null ).getServiceManager() ); + } + + + /** Bootstraps the initial component context from a native UNO installation. + + @throws Exception if things go awry. + @return a freshly bootstrapped component context. + + See also + cppuhelper/defaultBootstrap_InitialComponentContext(). + */ + public static final XComponentContext defaultBootstrap_InitialComponentContext() + throws Exception + { + return defaultBootstrap_InitialComponentContext( (String) null, (Map) null ); + } + /** + * Backwards compatibility stub. + * + * @param ini_file + * ini_file (may be null: uno.rc besides cppuhelper lib) + * @param bootstrap_parameters + * bootstrap parameters (maybe null) + * + * @throws Exception if things go awry. + * @return a freshly bootstrapped component context. + */ + public static final XComponentContext defaultBootstrap_InitialComponentContext( + String ini_file, Hashtable bootstrap_parameters ) + throws Exception + { + return defaultBootstrap_InitialComponentContext(ini_file, (Map) bootstrap_parameters); + + } + /** Bootstraps the initial component context from a native UNO installation. + + See also + cppuhelper/defaultBootstrap_InitialComponentContext(). + + @param ini_file + ini_file (may be null: uno.rc besides cppuhelper lib) + @param bootstrap_parameters + bootstrap parameters (maybe null) + + @throws Exception if things go awry. + @return a freshly bootstrapped component context. + */ + public static final XComponentContext defaultBootstrap_InitialComponentContext( + String ini_file, Map bootstrap_parameters ) + throws Exception + { + // jni convenience: easier to iterate over array than calling Hashtable + String pairs [] = null; + if (null != bootstrap_parameters) + { + pairs = new String [ 2 * bootstrap_parameters.size() ]; + int n = 0; + for (Map.Entry bootstrap_parameter : bootstrap_parameters.entrySet()) { + pairs[ n++ ] = bootstrap_parameter.getKey(); + pairs[ n++ ] = bootstrap_parameter.getValue(); + } + } + + if (! m_loaded_juh) + { + if ("The Android Project".equals(System.getProperty("java.vendor"))) + { + // Find out if we are configured with DISABLE_DYNLOADING or + // not. Try to load the lo-bootstrap shared library which + // won't exist in the DISABLE_DYNLOADING case. (And which will + // be already loaded otherwise, so nothing unexpected happens + // that case.) Yeah, this would be simpler if I just could be + // bothered to keep a separate branch for DISABLE_DYNLOADING + // on Android, merging in master periodically, until I know + // for sure whether it is what I want, or not. + + boolean disable_dynloading = false; + try { + System.loadLibrary( "lo-bootstrap" ); + } catch ( UnsatisfiedLinkError e ) { + disable_dynloading = true; + } + + if (!disable_dynloading) + { + NativeLibraryLoader.loadLibrary( Bootstrap.class.getClassLoader(), "juh" ); + } + } + else + { + NativeLibraryLoader.loadLibrary( Bootstrap.class.getClassLoader(), "juh" ); + } + m_loaded_juh = true; + } + return UnoRuntime.queryInterface( + XComponentContext.class, + cppuhelper_bootstrap( + ini_file, pairs, Bootstrap.class.getClassLoader() ) ); + } + + private static boolean m_loaded_juh = false; + private static native Object cppuhelper_bootstrap( + String ini_file, String bootstrap_parameters [], ClassLoader loader ) + throws Exception; + + /** + * Bootstraps the component context from a UNO installation. + * + * @throws BootstrapException if things go awry. + * + * @return a bootstrapped component context. + * + * @since UDK 3.1.0 + */ + public static final XComponentContext bootstrap() + throws BootstrapException { + + String[] defaultArgArray = getDefaultOptions(); + return bootstrap( defaultArgArray ); + } + + /** + * Bootstraps the component context from a UNO installation. + * + * @param argArray + * an array of strings - commandline options to start instance of + * soffice with + * @see #getDefaultOptions() + * + * @throws BootstrapException if things go awry. + * + * @return a bootstrapped component context. + * + * @since LibreOffice 5.1 + */ + public static final XComponentContext bootstrap( String[] argArray ) + throws BootstrapException { + + XComponentContext xContext = null; + + try { + // create default local component context + XComponentContext xLocalContext = + createInitialComponentContext( (Map) null ); + if ( xLocalContext == null ) + throw new BootstrapException( "no local component context!" ); + + // find office executable relative to this class's class loader + String sOffice = + System.getProperty( "os.name" ).startsWith( "Windows" ) ? + "soffice.exe" : "soffice"; + File fOffice = NativeLibraryLoader.getResource( + Bootstrap.class.getClassLoader(), sOffice ); + if ( fOffice == null ) + throw new BootstrapException( "no office executable found!" ); + + // create random pipe name + String sPipeName = "uno" + + Long.toString(randomPipeName.nextLong() & 0x7fffffffffffffffL); + + // create call with arguments + String[] cmdArray = new String[ argArray.length + 2 ]; + cmdArray[0] = fOffice.getPath(); + cmdArray[1] = ( "--accept=pipe,name=" + sPipeName + ";urp;" ); + + System.arraycopy( argArray, 0, cmdArray, 2, argArray.length ); + + // start office process + Process p = Runtime.getRuntime().exec( cmdArray ); + pipe( p.getInputStream(), System.out, "CO> " ); + pipe( p.getErrorStream(), System.err, "CE> " ); + + // initial service manager + XMultiComponentFactory xLocalServiceManager = + xLocalContext.getServiceManager(); + if ( xLocalServiceManager == null ) + throw new BootstrapException( "no initial service manager!" ); + + // create a URL resolver + XUnoUrlResolver xUrlResolver = + UnoUrlResolver.create( xLocalContext ); + + // connection string + String sConnect = "uno:pipe,name=" + sPipeName + + ";urp;StarOffice.ComponentContext"; + + // wait until office is started + for (int i = 0;; ++i) { + try { + // try to connect to office + Object context = xUrlResolver.resolve( sConnect ); + xContext = UnoRuntime.queryInterface( + XComponentContext.class, context); + if ( xContext == null ) + throw new BootstrapException( "no component context!" ); + break; + } catch ( com.sun.star.connection.NoConnectException ex ) { + // Wait 500 ms, then try to connect again, but do not wait + // longer than 5 min (= 600 * 500 ms) total: + if (i == 600) { + throw new BootstrapException(ex); + } + Thread.sleep( 500 ); + } + } + } catch ( BootstrapException e ) { + throw e; + } catch ( java.lang.RuntimeException e ) { + throw e; + } catch ( java.lang.Exception e ) { + throw new BootstrapException( e ); + } + + return xContext; + } + + /** + * Bootstraps the component context from a websocket location. + * + * @param url + * the ws:// or wss:// url of the websocket server + * + * @throws BootstrapException if things go awry. + * + * @return a bootstrapped component context. + * + * @since LibreOffice 24.2 + */ + public static final XComponentContext bootstrapWebsocketConnection( String url ) + throws BootstrapException { + + XComponentContext xContext = null; + + try { + // create default local component context + XComponentContext xLocalContext = + createInitialComponentContext( (Map) null ); + if ( xLocalContext == null ) + throw new BootstrapException( "no local component context!" ); + + // initial service manager + XMultiComponentFactory xLocalServiceManager = + xLocalContext.getServiceManager(); + if ( xLocalServiceManager == null ) + throw new BootstrapException( "no initial service manager!" ); + + // create a URL resolver + XUnoUrlResolver xUrlResolver = + UnoUrlResolver.create( xLocalContext ); + + // connection string + String sConnect = "uno:websocket" + + ",url=" + url + + ";urp;StarOffice.ComponentContext"; + + try { + // try to connect to office + Object xOfficeServiceManager = xUrlResolver.resolve(sConnect); + + xContext = UnoRuntime.queryInterface(XComponentContext.class, xOfficeServiceManager); + + if ( xContext == null ) + throw new BootstrapException( "no component context!" ); + } catch ( com.sun.star.connection.NoConnectException ex ) { + throw new BootstrapException(ex); + } + } catch ( BootstrapException e ) { + throw e; + } catch ( java.lang.RuntimeException e ) { + throw e; + } catch ( java.lang.Exception e ) { + throw new BootstrapException( e ); + } + + return xContext; + } + + + private static final Random randomPipeName = new Random(); + + private static void pipe( + final InputStream in, final PrintStream out, final String prefix ) { + + new Thread( "Pipe: " + prefix) { + @Override + public void run() { + try { + BufferedReader r = new BufferedReader( + new InputStreamReader(in, "UTF-8") ); + + for ( ; ; ) { + String s = r.readLine(); + if ( s == null ) { + break; + } + out.println( prefix + s ); + } + } catch ( UnsupportedEncodingException e ) { + e.printStackTrace( System.err ); + } catch ( java.io.IOException e ) { + e.printStackTrace( System.err ); + } + } + }.start(); + } +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/ridljar/com/sun/star/comp/helper/BootstrapException.java b/ridljar/com/sun/star/comp/helper/BootstrapException.java new file mode 100644 index 0000000000..11911bbc07 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/BootstrapException.java @@ -0,0 +1,82 @@ +/* + * 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.comp.helper; + +/** + * BootstrapException is a checked exception that wraps an exception + * thrown by the original target. + * + * @since UDK 3.1.0 + */ +public class BootstrapException extends java.lang.Exception { + + /** + * This field holds the target exception. + */ + private Exception m_target = null; + + /** + * Constructs a BootstrapException with null as + * the target exception. + */ + public BootstrapException() { + super(); + } + + /** + * Constructs a BootstrapException with the specified + * detail message. + * + * @param message the detail message + */ + public BootstrapException( String message ) { + super( message ); + } + + /** + * Constructs a BootstrapException with the specified + * detail message and a target exception. + * + * @param message the detail message + * @param target the target exception + */ + public BootstrapException( String message, Exception target ) { + super( message ); + m_target = target; + } + + /** + * Constructs a BootstrapException with a target exception. + * + * @param target the target exception + */ + public BootstrapException( Exception target ) { + super(); + m_target = target; + } + + /** + * Get the thrown target exception. + * + * @return the target exception + */ + public Exception getTargetException() { + return m_target; + } +} diff --git a/ridljar/com/sun/star/comp/helper/ComponentContext.java b/ridljar/com/sun/star/comp/helper/ComponentContext.java new file mode 100644 index 0000000000..34649d5633 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/ComponentContext.java @@ -0,0 +1,308 @@ +/* + * 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.comp.helper; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.Any; + +import com.sun.star.uno.DeploymentException; +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.EventObject; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; + + + +class Disposer implements XEventListener +{ + private final XComponent m_xComp; + + + Disposer( XComponent xComp ) + { + m_xComp = xComp; + } + + public void disposing( EventObject Source ) + { + m_xComp.dispose(); + } +} + +/** Component context implementation. +*/ +public class ComponentContext implements XComponentContext, XComponent +{ + private static final boolean DEBUG = false; + private static final String SMGR_NAME = "/singletons/com.sun.star.lang.theServiceManager"; + private static final String TDMGR_NAME = "/singletons/com.sun.star.reflection.theTypeDescriptionManager"; + + private Map m_table; + private XComponentContext m_xDelegate; + + private XMultiComponentFactory m_xSMgr; + private boolean m_bDisposeSMgr; + + private ArrayList m_eventListener; + + public ComponentContext( Hashtable table, XComponentContext xDelegate ) + { + this((Map) table, xDelegate); + } + + /** Ctor to create a component context passing a hashtable for values and a delegator + reference. Entries of the passed hashtable are either direct values or + ComponentContextEntry objects. + + @param table + entries + @param xDelegate + if values are not found, request is delegated to this object + */ + public ComponentContext( Map table, XComponentContext xDelegate ) + { + m_eventListener = new ArrayList(); + m_table = table; + m_xDelegate = xDelegate; + m_xSMgr = null; + m_bDisposeSMgr = false; + + Object o = table.get( SMGR_NAME ); + if (o != null) + { + if (o instanceof ComponentContextEntry) + { + o = ((ComponentContextEntry)o).m_value; + } + m_xSMgr = UnoRuntime.queryInterface( + XMultiComponentFactory.class, o ); + } + if (m_xSMgr != null) + { + m_bDisposeSMgr = true; + } + else if (m_xDelegate != null) + { + m_xSMgr = m_xDelegate.getServiceManager(); + } + + // listen for delegate + XComponent xComp = UnoRuntime.queryInterface( + XComponent.class, m_xDelegate ); + if (xComp != null) + { + xComp.addEventListener( new Disposer( this ) ); + } + } + + // XComponentContext impl + + public Object getValueByName( String rName ) + { + Object o = m_table.get( rName ); + if (o == null) + { + if (m_xDelegate != null) + { + return m_xDelegate.getValueByName( rName ); + } + else + { + return Any.VOID; + } + } + + if (!(o instanceof ComponentContextEntry)) + { + // direct value in map + return o; + } + + ComponentContextEntry entry = (ComponentContextEntry)o; + if (entry.m_lateInit == null) + { + return entry.m_value; + } + + Object xInstance = null; + try + { + String serviceName = (String)entry.m_lateInit; + if (serviceName != null) + { + if (m_xSMgr != null) + { + xInstance = m_xSMgr.createInstanceWithContext( serviceName, this ); + } + else + { + if (DEBUG) + System.err.println( "### no service manager instance for late init of singleton instance \"" + rName + "\"!" ); + } + } + else + { + XSingleComponentFactory xCompFac = UnoRuntime.queryInterface( XSingleComponentFactory.class, entry.m_lateInit ); + if (xCompFac != null) + { + xInstance = xCompFac.createInstanceWithContext( this ); + } + else + { + if (DEBUG) + System.err.println( "### neither service name nor service factory given for late init of singleton instance \"" + rName + "\"!" ); + } + } + } + catch (com.sun.star.uno.Exception exc) + { + if (DEBUG) + System.err.println( "### exception occurred on late init of singleton instance \"" + rName + "\": " + exc.getMessage() ); + } + + if (xInstance != null) + { + synchronized (entry) + { + if (entry.m_lateInit != null) + { + entry.m_value = xInstance; + entry.m_lateInit = null; + } + else // inited in the meantime + { + // dispose fresh service instance + XComponent xComp = UnoRuntime.queryInterface( + XComponent.class, xInstance ); + if (xComp != null) + { + xComp.dispose(); + } + } + } + } + else + { + if (DEBUG) + System.err.println( "### failed late init of singleton instance \"" + rName + "\"!" ); + } + return entry.m_value; + } + + public XMultiComponentFactory getServiceManager() + { + if (m_xSMgr == null) + { + throw new DeploymentException( + "null component context service manager" ); + } + return m_xSMgr; + } + + // XComponent impl + + public void dispose() + { + if (DEBUG) + System.err.print( "> disposing context " + this ); + + // fire events + EventObject evt = new EventObject( this ); + for (XEventListener listener : m_eventListener) + { + listener.disposing( evt ); + } + m_eventListener.clear(); + + XComponent tdmgr = null; + // dispose values, then service manager, then typedescription manager + for (Map.Entry entry : m_table.entrySet()) + { + String name = entry.getKey(); + if (! name.equals( SMGR_NAME )) + { + Object o = entry.getValue(); + if (o instanceof ComponentContextEntry) + { + o = ((ComponentContextEntry)o).m_value; + } + + XComponent xComp = UnoRuntime.queryInterface( XComponent.class, o ); + if (xComp != null) + { + if (name.equals( TDMGR_NAME )) + { + tdmgr = xComp; + } + else + { + xComp.dispose(); + } + } + } + } + m_table.clear(); + + // smgr + if (m_bDisposeSMgr) + { + XComponent xComp = UnoRuntime.queryInterface( + XComponent.class, m_xSMgr ); + if (xComp != null) + { + xComp.dispose(); + } + } + m_xSMgr = null; + + // tdmgr + if (tdmgr != null) + { + tdmgr.dispose(); + } + + if (DEBUG) + System.err.println( "... finished" ); + } + + public void addEventListener( XEventListener xListener ) + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException( "Listener must not be null" ); + if (m_eventListener.contains( xListener )) + throw new com.sun.star.uno.RuntimeException( "Listener already registered." ); + + m_eventListener.add( xListener ); + } + + public void removeEventListener( XEventListener xListener ) + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException( "Listener must not be null" ); + if (! m_eventListener.contains( xListener )) + throw new com.sun.star.uno.RuntimeException( "Listener is not registered." ); + + m_eventListener.remove( xListener ); + } +} diff --git a/ridljar/com/sun/star/comp/helper/ComponentContextEntry.java b/ridljar/com/sun/star/comp/helper/ComponentContextEntry.java new file mode 100644 index 0000000000..aa402d29c6 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/ComponentContextEntry.java @@ -0,0 +1,64 @@ +/* + * 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.comp.helper; + +/** Component context entry for constructing ComponentContext objects. +

+ A ComponentContextEntry is separated into a late-init and direct-value + purpose. + The first one is commonly used for singleton objects of the component + context, that are raised on first-time retrieval of the key. + You have to pass a com.sun.star.lang.XSingleComponentFactory + or string (=> service name) object for this. +

+*/ +public class ComponentContextEntry +{ + /** if late init of service instance, set service name (String) or + component factory (XSingleComponentFactory), null otherwise + */ + public Object m_lateInit; + /** set entry value + */ + public Object m_value; + + /** Creating a late-init singleton entry component context entry. + The second parameter will be ignored and overwritten during + instantiation of the singleton instance. + + @param lateInit + object factory or service string + @param value + pass null (dummy separating from second constructor signature) + */ + public ComponentContextEntry( Object lateInit, Object value ) + { + this.m_lateInit = lateInit; + this.m_value = value; + } + /** Creating a direct value component context entry. + + @param value + pass null + */ + public ComponentContextEntry( Object value ) + { + this.m_lateInit = null; + this.m_value = value; + } +} diff --git a/ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java b/ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java new file mode 100644 index 0000000000..035644efa0 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java @@ -0,0 +1,175 @@ +// -*- 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.comp.helper; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * @deprecated use class Bootstrap bootstrapping a native UNO installation + * and use the shared library loader service. + * + * The SharedLibraryLoader class provides the functionality of the com.sun.star.loader.SharedLibrary + * service. + * + * See also UNOIDL com.sun.star.lang.ServiceManager. + * + * @see com.sun.star.loader.SharedLibrary + */ +@Deprecated +public class SharedLibraryLoader { + /** + * The default library which contains the SharedLibraryLoader component + */ + public static final String DEFAULT_LIBRARY = "shlibloader.uno"; + + /** + * The default implementation name + */ + public static final String DEFAULT_IMPLEMENTATION = "com.sun.star.comp.stoc.DLLComponentLoader"; + + static { + if ("The Android Project".equals(System.getProperty("java.vendor"))) { + // See corresponding code in + // javaunohelper/com/sun/star/comp/helper/Bootstrap.java for more + // comments. + + boolean disable_dynloading = false; + try { + System.loadLibrary("lo-bootstrap"); + } catch (UnsatisfiedLinkError e) { + disable_dynloading = true; + } + + if (!disable_dynloading) + System.loadLibrary("juh"); + } else + System.loadLibrary("juh"); + } + + private static native boolean component_writeInfo( + String libName, XMultiServiceFactory smgr, XRegistryKey regKey, + ClassLoader loader ); + + private static native Object component_getFactory( + String libName, String implName, XMultiServiceFactory smgr, + XRegistryKey regKey, ClassLoader loader ); + + /** + * Supplies the ServiceFactory of the default SharedLibraryLoader. + * The defaults are "shlibloader.uno" + * for the library and "com.sun.star.comp.stoc.DLLComponentLoader" + * for the component name. + * + * See also UNOIDL com.sun.star.lang.ServiceManager and + * com.sun.star.registry.RegistryKey. + * + * @return the factory for the "com.sun.star.comp.stoc.DLLComponentLoader" component. + * @param smgr the ServiceManager + * @param regKey the root registry key + * @see com.sun.star.loader.SharedLibrary + */ + public static XSingleServiceFactory getServiceFactory( + XMultiServiceFactory smgr, + XRegistryKey regKey ) + { + return UnoRuntime.queryInterface( + XSingleServiceFactory.class, + component_getFactory( + DEFAULT_LIBRARY, DEFAULT_IMPLEMENTATION, smgr, regKey, + SharedLibraryLoader.class.getClassLoader() ) ); + } + + /** + * Loads and returns a specific factory for a given library and implementation name. + * + * See also UNOIDL com.sun.star.lang.ServiceManager and + * com.sun.star.registry.RegistryKey. + * + * @return the factory of the component + * @param libName the name of the shared library + * @param impName the implementation name of the component + * @param smgr the ServiceManager + * @param regKey the root registry key + * @see com.sun.star.loader.SharedLibrary + */ + public static XSingleServiceFactory getServiceFactory( + String libName, + String impName, + XMultiServiceFactory smgr, + XRegistryKey regKey ) + { + return UnoRuntime.queryInterface( + XSingleServiceFactory.class, + component_getFactory( + libName, impName, smgr, regKey, + SharedLibraryLoader.class.getClassLoader() ) ); + } + + /** + * Registers the SharedLibraryLoader under a RegistryKey. + * + * See also UNOIDL com.sun.star.lang.ServiceManager and + * com.sun.star.registry.RegistryKey. + * + * @return true if the registration was successful - otherwise false + * @param smgr the ServiceManager + * @param regKey the root key under that the component should be registered + * @see com.sun.star.loader.SharedLibrary + */ + public static boolean writeRegistryServiceInfo( + com.sun.star.lang.XMultiServiceFactory smgr, + com.sun.star.registry.XRegistryKey regKey ) + { + return component_writeInfo( + DEFAULT_LIBRARY, smgr, regKey, + SharedLibraryLoader.class.getClassLoader() ); + } + + /** + * Registers the SharedLibraryLoader under a RegistryKey. + * + * See also UNOIDL com.sun.star.lang.ServiceManager and + * com.sun.star.registry.RegistryKey. + * + * @return true if the registration was successful - otherwise false + * @param libName name of the shared library + * @param smgr the ServiceManager + * @param regKey the root key under that the component should be registered + * @throws com.sun.star.registry.InvalidRegistryException + * if the registry is not valid. + * + * @see com.sun.star.loader.SharedLibrary + */ + public static boolean writeRegistryServiceInfo( + String libName, + com.sun.star.lang.XMultiServiceFactory smgr, + com.sun.star.registry.XRegistryKey regKey ) + + throws com.sun.star.registry.InvalidRegistryException, + com.sun.star.uno.RuntimeException + { + return component_writeInfo( + libName, smgr, regKey, SharedLibraryLoader.class.getClassLoader() ); + } +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/ridljar/com/sun/star/comp/loader/FactoryHelper.java b/ridljar/com/sun/star/comp/loader/FactoryHelper.java new file mode 100644 index 0000000000..fe56f594f2 --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/FactoryHelper.java @@ -0,0 +1,502 @@ +/* -*- 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.comp.loader; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.Type; + + +/** + * The purpose of this class to help component implementation. + * + *

This class has default implementations for getServiceFactory + * and writeRegistryServiceInfo.

+ * + * @see com.sun.star.lang.XMultiServiceFactory + * @see com.sun.star.lang.XServiceInfo + * @see com.sun.star.lang.XSingleServiceFactory + * @see com.sun.star.registry.XRegistryKey + * @since UDK1.0 + */ +public class FactoryHelper { + + private static final boolean DEBUG = false; + // the factory + protected static class Factory + implements XSingleServiceFactory, XSingleComponentFactory, XServiceInfo, + XTypeProvider { + + protected XMultiServiceFactory _xMultiServiceFactory; + protected XRegistryKey _xRegistryKey; + protected int _nCode; + protected Constructor _constructor; + protected String _implName; + protected String _serviceName; + + protected Factory(Class implClass, + String serviceName, + XMultiServiceFactory xMultiServiceFactory, + XRegistryKey xRegistryKey) + { + _xMultiServiceFactory = xMultiServiceFactory; + _xRegistryKey = xRegistryKey; + _implName = implClass.getName(); + _serviceName = serviceName; + + Constructor constructors[] = implClass.getConstructors(); + for(int i = 0; i < constructors.length && _constructor == null; ++i) { + Class parameters[] = constructors[i].getParameterTypes(); + + if(parameters.length == 3 + && parameters[0].equals(XComponentContext.class) + && parameters[1].equals(XRegistryKey.class) + && parameters[2].equals(Object[].class)) { + _nCode = 0; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XComponentContext.class) + && parameters[1].equals(XRegistryKey.class)) { + _nCode = 1; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XComponentContext.class) + && parameters[1].equals(Object[].class)) { + _nCode = 2; + _constructor = constructors[i]; + } + else if(parameters.length == 1 + && parameters[0].equals(XComponentContext.class)) { + _nCode = 3; + _constructor = constructors[i]; + } + // depr + else if(parameters.length == 3 + && parameters[0].equals(XMultiServiceFactory.class) + && parameters[1].equals(XRegistryKey.class) + && parameters[2].equals(Object[].class)) { + _nCode = 4; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XMultiServiceFactory.class) + && parameters[1].equals(XRegistryKey.class)) { + _nCode = 5; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XMultiServiceFactory.class) + && parameters[1].equals(Object[].class)) { + _nCode = 6; + _constructor = constructors[i]; + } + else if(parameters.length == 1 + && parameters[0].equals(XMultiServiceFactory.class)) { + _nCode = 7; + _constructor = constructors[i]; + } + else if(parameters.length == 1 + && parameters[0].equals(Object[].class)) { + _nCode = 8; + _constructor = constructors[i]; + } + else if(parameters.length == 0) { + _nCode = 9; + _constructor = constructors[i]; + } + } + + if(_constructor == null) // have not found a usable constructor + throw new com.sun.star.uno.RuntimeException(getClass().getName() + " can not find a usable constructor"); + } + + private final XMultiServiceFactory getSMgr( XComponentContext xContext ) + { + if (xContext != null) + { + return UnoRuntime.queryInterface( + XMultiServiceFactory.class, xContext.getServiceManager() ); + } + else + { + return _xMultiServiceFactory; + } + } + + // XComponentContext impl + + public Object createInstanceWithContext( + XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object args[]; + switch (_nCode) + { + case 0: + args = new Object [] { xContext, _xRegistryKey, new Object[ 0 ] }; + break; + case 1: + args = new Object [] { xContext, _xRegistryKey }; + break; + case 2: + args = new Object [] { xContext, new Object[ 0 ] }; + break; + case 3: + args = new Object [] { xContext }; + break; + case 4: + args = new Object [] { getSMgr( xContext ), _xRegistryKey, new Object[ 0 ] }; + break; + case 5: + args = new Object [] { getSMgr( xContext ), _xRegistryKey }; + break; + case 6: + args = new Object [] { getSMgr( xContext ), new Object[ 0 ] }; + break; + case 7: + args = new Object [] { getSMgr( xContext ) }; + break; + case 8: + args = new Object [] { new Object[ 0 ] }; + break; + default: + args = new Object [ 0 ]; + break; + } + + try { + return _constructor.newInstance( args ); + } catch (InvocationTargetException invocationTargetException) { + Throwable targetException = invocationTargetException.getCause(); + + if (targetException instanceof java.lang.RuntimeException) + throw (java.lang.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.Exception) + throw (com.sun.star.uno.Exception)targetException; + else if (targetException instanceof com.sun.star.uno.RuntimeException) + throw (com.sun.star.uno.RuntimeException)targetException; + else + throw new com.sun.star.uno.Exception( targetException ); + } catch (IllegalAccessException illegalAccessException) { + throw new com.sun.star.uno.Exception( illegalAccessException ); + } catch (InstantiationException instantiationException) { + throw new com.sun.star.uno.Exception( instantiationException ); + } + } + + public Object createInstanceWithArgumentsAndContext( + Object rArguments[], XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object args[]; + + boolean bInitCall = true; + switch (_nCode) + { + case 0: + args = new Object [] { xContext, _xRegistryKey, rArguments }; + bInitCall = false; + break; + case 1: + args = new Object [] { xContext, _xRegistryKey }; + break; + case 2: + args = new Object [] { xContext, rArguments }; + bInitCall = false; + break; + case 3: + args = new Object [] { xContext }; + break; + case 4: + args = new Object [] { getSMgr( xContext ), _xRegistryKey, rArguments }; + bInitCall = false; + break; + case 5: + args = new Object [] { getSMgr( xContext ), _xRegistryKey }; + break; + case 6: + args = new Object [] { getSMgr( xContext ), rArguments }; + bInitCall = false; + break; + case 7: + args = new Object [] { getSMgr( xContext ) }; + break; + case 8: + args = new Object [] { rArguments }; + bInitCall = false; + break; + default: + args = new Object [ 0 ]; + break; + } + + try { + Object instance = _constructor.newInstance( args ); + if (bInitCall) + { + XInitialization xInitialization = UnoRuntime.queryInterface( + XInitialization.class, instance ); + if (xInitialization != null) + { + xInitialization.initialize( rArguments ); + } + } + return instance; + } catch (InvocationTargetException invocationTargetException) { + Throwable targetException = invocationTargetException.getCause(); + + if (targetException instanceof java.lang.RuntimeException) + throw (java.lang.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.Exception) + throw (com.sun.star.uno.Exception)targetException; + else if (targetException instanceof com.sun.star.uno.RuntimeException) + throw (com.sun.star.uno.RuntimeException)targetException; + else + throw new com.sun.star.uno.Exception( targetException ); + } catch (IllegalAccessException illegalAccessException) { + throw new com.sun.star.uno.Exception( illegalAccessException ); + } catch (InstantiationException instantiationException) { + throw new com.sun.star.uno.Exception( instantiationException ); + } + } + + /** + * Creates an instance of the desired service. + * + * @return returns an instance of the desired service. + * @see com.sun.star.lang.XSingleServiceFactory + */ + public Object createInstance() + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return createInstanceWithContext( null ); + } + + /** + * Creates an instance of the desired service. + * + * @param args the args given to the constructor of the service. + * @return returns an instance of the desired service. + * + * @see com.sun.star.lang.XSingleServiceFactory + */ + public Object createInstanceWithArguments(Object[] args) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return createInstanceWithArgumentsAndContext( args, null ); + } + + /** + * Gives the supported services. + * + * @return returns an array of supported services. + * @see com.sun.star.lang.XServiceInfo + */ + public String[] getSupportedServiceNames() throws com.sun.star.uno.RuntimeException { + return new String[]{_serviceName}; + } + + /** + * Gives the implementation name. + * + * @return returns the implementation name. + * @see com.sun.star.lang.XServiceInfo + */ + public String getImplementationName() throws com.sun.star.uno.RuntimeException { + return _implName; + } + + /** + * Indicates if the given service is supported. + * + * @return returns true if the given service is supported. + * @see com.sun.star.lang.XServiceInfo + */ + public boolean supportsService(String serviceName) throws com.sun.star.uno.RuntimeException { + String services[] = getSupportedServiceNames(); + + boolean found = false; + + for(int i = 0; i < services.length && !found; ++i) + found = services[i].equals(serviceName); + + return found; + } + + //XTypeProvider + public byte[] getImplementationId() + { + return new byte[0]; + } + //XTypeProvider + public com.sun.star.uno.Type[] getTypes() + { + Type[] t = new Type[] { + new Type(XSingleServiceFactory.class), + new Type(XSingleComponentFactory.class), + new Type(XServiceInfo.class), + new Type(XTypeProvider.class) + }; + return t; + } + + } + + /** + * Creates a factory for the given class. + * + * @param implClass the implementing class. + * @param multiFactory the given multi service factory (service manager). + * @param regKey the given registry key. + * @return returns a factory. + * + * @see com.sun.star.lang.XServiceInfo + * @deprecated as of UDK 1.0 + */ + @Deprecated + public static XSingleServiceFactory getServiceFactory(Class implClass, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + try { + Field serviceName ; + + try { + serviceName = implClass.getField("__serviceName"); + } catch(NoSuchFieldException noSuchFieldExceptio) { + serviceName = implClass.getField("serviceName"); // old style + } + + xSingleServiceFactory = new Factory(implClass, (String)serviceName.get(null), multiFactory, regKey); + } catch(NoSuchFieldException noSuchFieldException) { + System.err.println("##### FactoryHelper.getServiceFactory - exception:" + noSuchFieldException); + } catch(IllegalAccessException illegalAccessException) { + System.err.println("##### FactoryHelper.getServiceFactory - exception:" + illegalAccessException); + } + + return xSingleServiceFactory; + } + + /** + * Creates a factory for the given class. + * + * @param implClass the implementing class. + * @param serviceName the service name of the implementing class. + * @param multiFactory the given multi service factory (service manager). + * @param regKey the given registry key. + * + * @return returns a factory. + * @see com.sun.star.lang.XServiceInfo + */ + public static XSingleServiceFactory getServiceFactory(Class implClass, + String serviceName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + return new Factory(implClass, serviceName, multiFactory, regKey); + } + + /** + * Creates a factory for the given class. + * + * @param implClass the implementing class. + * @return returns a factory object. + */ + public static Object createComponentFactory( Class implClass, String serviceName ) + { + return new Factory( implClass, serviceName, null, null ); + } + + /** + * Writes the registration data into the registry key. + * + * @param implName the name of the implementing class. + * @param serviceName the service name. + * @param regKey the given registry key. + * @return success. + * + * @see com.sun.star.lang.XServiceInfo + */ + public static boolean writeRegistryServiceInfo(String implName, String serviceName, XRegistryKey regKey) { + boolean result = false; + + try { + XRegistryKey newKey = regKey.createKey("/" + implName + "/UNO/SERVICES"); + + newKey.createKey(serviceName); + + result = true; + } + catch (Exception ex) { + System.err.println(">>>Connection_Impl.writeRegistryServiceInfo " + ex); + } + + return result; + } + + /** + * Writes the registration data into the registry key. + * + *

Several services are supported.

+ * + * @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; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/loader/JavaLoader.java b/ridljar/com/sun/star/comp/loader/JavaLoader.java new file mode 100644 index 0000000000..42dee0da5c --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/JavaLoader.java @@ -0,0 +1,439 @@ +/* -*- 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.comp.loader; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.net.URLDecoder; + +import com.sun.star.loader.CannotActivateFactoryException; +import com.sun.star.loader.XImplementationLoader; + +import com.sun.star.registry.CannotRegisterImplementationException; +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XInitialization; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.beans.XPropertySet; +import com.sun.star.util.XMacroExpander; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.lib.util.StringHelper; + +import com.sun.star.uno.AnyConverter; + + +/** + * The JavaLoader class provides the functionality of the + * com.sun.star.loader.Java service. + * + *

Therefore the JavaLoader activates external UNO components + * which are implemented in Java.

+ * + *

The loader is used by the ServiceManager.

+ * + * @see com.sun.star.loader.XImplementationLoader + * @see com.sun.star.loader.Java + * @see com.sun.star.comp.servicemanager.ServiceManager + * @since UDK1.0 + */ +public class JavaLoader implements XImplementationLoader, + XServiceInfo, + XInitialization +{ + private static final boolean DEBUG = false; + + private static final void DEBUG(String dbg) { + if (DEBUG) System.err.println( dbg ); + } + + private static String[] supportedServices = { + "com.sun.star.loader.Java" + }; + + protected XMultiServiceFactory multiServiceFactory = null; + + private XMacroExpander m_xMacroExpander = null; + private static final String EXPAND_PROTOCOL_PREFIX = "vnd.sun.star.expand:"; + + /** + * Expands macrofied url using the macro expander singleton. + */ + private String expand_url( String url ) throws RuntimeException + { + if (url == null || !url.startsWith( EXPAND_PROTOCOL_PREFIX )) { + return url; + } + try { + if (m_xMacroExpander == null) { + XPropertySet xProps = + UnoRuntime.queryInterface( + XPropertySet.class, multiServiceFactory ); + if (xProps == null) { + throw new com.sun.star.uno.RuntimeException( + "service manager does not support XPropertySet!", + this ); + } + XComponentContext xContext = (XComponentContext) + AnyConverter.toObject( + new Type( XComponentContext.class ), + xProps.getPropertyValue( "DefaultContext" ) ); + m_xMacroExpander = (XMacroExpander)AnyConverter.toObject( + new Type( XMacroExpander.class ), + xContext.getValueByName( + "/singletons/com.sun.star.util.theMacroExpander" ) + ); + } + // decode uric class chars + String macro = URLDecoder.decode( + StringHelper.replace( + url.substring( EXPAND_PROTOCOL_PREFIX.length() ), + '+', "%2B" ), "UTF-8" ); + // expand macro string + String ret = m_xMacroExpander.expandMacros( macro ); + if (DEBUG) { + System.err.println( + "JavaLoader.expand_url(): " + url + " => " + + macro + " => " + ret ); + } + return ret; + } catch (com.sun.star.uno.Exception exc) { + throw new com.sun.star.uno.RuntimeException(exc, + exc.getMessage(), this ); + } catch (java.lang.Exception exc) { + throw new com.sun.star.uno.RuntimeException(exc, + exc.getMessage(), this ); + } + } + + /** + * Default constructor. + * + *

Creates a new instance of the JavaLoader class.

+ */ + public JavaLoader() {} + + /** + * Creates a new JavaLoader object. + * + *

The specified com.sun.star.lang.XMultiServiceFactory is + * the ServiceManager service which can be delivered to all + * components the JavaLoader is loading.

+ * + *

To set the MultiServiceFactory you can use the + * com.sun.star.lang.XInitialization interface, either.

+ * + * @param factory the ServiceManager. + * @see com.sun.star.comp.servicemanager.ServiceManager + * @see com.sun.star.lang.XInitialization + */ + public JavaLoader(XMultiServiceFactory factory) { + multiServiceFactory = factory; + } + + /** + * Unlike the original intention, the method could be called every time a + * new com.sun.star.lang.XMultiServiceFactory should be set at + * the loader. + * + * @param args - the first parameter (args[0]) specifies the ServiceManager. + * @see com.sun.star.lang.XInitialization + * @see com.sun.star.comp.servicemanager.ServiceManager + */ + public void initialize( java.lang.Object[] args ) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + if (args.length == 0) + throw new com.sun.star.lang.IllegalArgumentException("No arguments specified"); + + try { + multiServiceFactory = (XMultiServiceFactory) AnyConverter.toObject( + new Type(XMultiServiceFactory.class), args[0]); + } catch (ClassCastException castEx) { + throw new com.sun.star.lang.IllegalArgumentException(castEx, + "The argument must be an instance of XMultiServiceFactory"); + } + } + + /** + * Supplies the implementation name of the component. + * + * @return the implementation name - here the class name. + * @see com.sun.star.lang.XServiceInfo + */ + public String getImplementationName() + throws com.sun.star.uno.RuntimeException + { + return getClass().getName(); + } + + /** + * Verifies if a given service is supported by the component. + * + * @param serviceName the name of the service that should be checked. + * @return true,if service is supported - otherwise false. + * + * @see com.sun.star.lang.XServiceInfo + */ + public boolean supportsService(String serviceName) + throws com.sun.star.uno.RuntimeException + { + for (String supportedService : supportedServices) { + if (supportedService.equals(serviceName)) { + return true; + } + } + return false; + } + + /** + * Supplies a list of all service names supported by the component. + * + * @return a String array with all supported services. + * @see com.sun.star.lang.XServiceInfo + */ + public String[] getSupportedServiceNames() + throws com.sun.star.uno.RuntimeException + { + return supportedServices; + } + + /** + * Provides a components factory. + * + *

The JavaLoader tries to load the class first. If a + * location URL is given the RegistrationClassFinder is used to load the + * class. Otherwise the class is loaded through the Class.forName method.

+ * + *

To get the factory the inspects the class for the optional static member + * functions __getServiceFactory resp. getServiceFactory (DEPRECATED).

+ * + * @param implementationName the implementation (class) name of the component. + * @param implementationLoaderUrl the URL of the implementation loader. Not used. + * @param locationUrl points to an archive (JAR file) which contains a component. + * @param xKey registry key. + * @return the factory for the component (@see com.sun.star.lang.XSingleServiceFactory) + * + * @see com.sun.star.loader.XImplementationLoader + * @see com.sun.star.comp.loader.RegistrationClassFinder + */ + public java.lang.Object activate( String implementationName, + String implementationLoaderUrl, + String locationUrl, + XRegistryKey xKey ) + throws CannotActivateFactoryException, + com.sun.star.uno.RuntimeException + { + locationUrl = expand_url( locationUrl ); + + Object returnObject = null; + Class clazz ; + + DEBUG("try to get factory for " + implementationName); + + // First we must get the class of the implementation + // 1. If a location URL is given it is assumed that this points to a JAR file. + // The components class name is stored in the manifest file. + // 2. If only the implementation name is given, the class is loaded with the + // Class.forName() method. This is a hack to load bootstrap components. + // Normally a string must no be null. + try { + if ( locationUrl != null ) { + clazz = RegistrationClassFinder.find( locationUrl ); + if (clazz == null) { + throw new CannotActivateFactoryException( + "Cannot activate jar " + locationUrl); + } + } else { + clazz = Class.forName( implementationName ); + if (clazz == null) { + throw new CannotActivateFactoryException( + "Cannot find class " + implementationName); + } + } + } catch (java.net.MalformedURLException e) { + throw new CannotActivateFactoryException(e, "Can not activate factory because " + e); + } catch (java.io.IOException e) { + throw new CannotActivateFactoryException(e, "Can not activate factory because " + e); + } catch (java.lang.ClassNotFoundException e) { + throw new CannotActivateFactoryException(e, "Can not activate factory because " + e); + } + + Class[] paramTypes = {String.class, XMultiServiceFactory.class, XRegistryKey.class}; + Object[] params = { implementationName, multiServiceFactory, xKey }; + + // try to get factory from implementation class + // latest style: use the public static method __getComponentFactory + // - new style: use the public static method __getServiceFactory + // - old style: use the public static method getServiceFactory ( DEPRECATED ) + + Method compfac_method = null; + try { + compfac_method = clazz.getMethod( + "__getComponentFactory", new Class [] { String.class } ); + } catch ( NoSuchMethodException noSuchMethodEx) { + } catch ( SecurityException secEx) { + } + + Method method = null; + if (null == compfac_method) { + try { + method = clazz.getMethod("__getServiceFactory", paramTypes); + } catch ( NoSuchMethodException noSuchMethodEx) { + } catch ( SecurityException secEx) { + } + } + + try { + if (null != compfac_method) { + Object ret = compfac_method.invoke( clazz, new Object [] { implementationName } ); + if (!(ret instanceof XSingleComponentFactory)) + throw new CannotActivateFactoryException( + "No factory object for " + implementationName ); + + return ret; + } + else { + if ( method == null ) + method = clazz.getMethod("getServiceFactory", paramTypes); + + Object oRet = method.invoke(clazz, params); + + if ( (oRet != null) && (oRet instanceof XSingleServiceFactory) ) + returnObject = oRet; + } + } catch ( NoSuchMethodException e) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } catch ( SecurityException e) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } catch ( IllegalAccessException e ) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } + catch ( IllegalArgumentException e ) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } catch ( InvocationTargetException e ) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } + + return returnObject; + } + + /** + * Registers the component in a registry under a given root key. + * + *

If the component supports the optional + * methods __writeRegistryServiceInfo, writeRegistryServiceInfo (DEPRECATED), + * the call is delegated to that method. Otherwise a default registration + * will be accomplished.

+ * + * @param regKey the root key under that the component should be registered. + * @param implementationLoaderUrl specifies the loader, the component is loaded by. + * @param locationUrl points to an archive (JAR file) which contains a component. + * @return true if registration is successfully - otherwise false. + */ + public boolean writeRegistryInfo( XRegistryKey regKey, + String implementationLoaderUrl, + String locationUrl ) + throws CannotRegisterImplementationException, + com.sun.star.uno.RuntimeException + { + locationUrl = expand_url( locationUrl ); + + boolean success = false; + + try { + + Class clazz = RegistrationClassFinder.find(locationUrl); + if (null == clazz) + throw new CannotRegisterImplementationException( + "Cannot determine registration class!" ); + + Class[] paramTypes = { XRegistryKey.class }; + Object[] params = { regKey }; + + Method method = clazz.getMethod("__writeRegistryServiceInfo", paramTypes); + Object oRet = method.invoke(clazz, params); + + if ( (oRet != null) && (oRet instanceof Boolean) ) + success = ((Boolean) oRet).booleanValue(); + } catch (Exception e) { + throw new CannotRegisterImplementationException(e, e.toString()); + } + + return success; + } + + /** + * Supplies the factory for the JavaLoader. + * + * @param implName the name of the desired component. + * @param multiFactory the ServiceManager is delivered to the factory. + * @param regKey not used - can be null. + * @return the factory for the JavaLoader. + */ + public static XSingleServiceFactory getServiceFactory( String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + if ( implName.equals(JavaLoader.class.getName()) ) + return new JavaLoaderFactory( multiFactory ); + + return null; + } + + /** + * Registers the JavaLoader at the registry. + * + * @param regKey root key under which the JavaLoader should be registered. + * @return true if registration succeeded - otherwise false. + */ + public static boolean writeRegistryServiceInfo(XRegistryKey regKey) { + boolean result = false; + + try { + XRegistryKey newKey = regKey.createKey("/" + JavaLoader.class.getName() + "/UNO/SERVICE"); + + for (String supportedService : supportedServices) { + newKey.createKey(supportedService); + } + + result = true; + } catch (Exception ex) { + if (DEBUG) System.err.println(">>>JavaLoader.writeRegistryServiceInfo " + ex); + } + + return result; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java b/ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java new file mode 100644 index 0000000000..edf4f7a3a4 --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java @@ -0,0 +1,90 @@ +/* -*- 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.comp.loader; + +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; + + +public class JavaLoaderFactory implements XSingleServiceFactory, XServiceInfo { + + private static String[] supportedServices = { + "com.sun.star.loader.Java", + "com.sun.star.loader.Java2" + }; + + protected XMultiServiceFactory multiServiceFactory = null; + + public JavaLoaderFactory(XMultiServiceFactory factory) { + multiServiceFactory = factory; + } + + public java.lang.Object createInstance() + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return new JavaLoader(multiServiceFactory); + } + + public java.lang.Object createInstanceWithArguments( java.lang.Object[] args ) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + JavaLoader loader = new JavaLoader(); + loader.initialize(args); + + return loader; + } + + /** + * Implements the XServiceInfo interface. + */ + public String getImplementationName() + throws com.sun.star.uno.RuntimeException + { + return JavaLoader.class.getName(); + } + + /** + * Implements the XServiceInfo interface. + */ + public boolean supportsService(String serviceName) + throws com.sun.star.uno.RuntimeException + { + for (String supportedService : supportedServices) { + if (supportedService.equals(serviceName)) { + return true; + } + } + return false; + } + + /** + * Implements the XServiceInfo interface. + */ + public String[] getSupportedServiceNames() + throws com.sun.star.uno.RuntimeException + { + return supportedServices; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java b/ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java new file mode 100644 index 0000000000..e7679da167 --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java @@ -0,0 +1,69 @@ +/* -*- 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.comp.loader; + +import com.sun.star.lib.unoloader.UnoClassLoader; +import com.sun.star.lib.util.WeakMap; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.jar.Attributes; + +final class RegistrationClassFinder { + public static Class find(String locationUrl) + throws ClassNotFoundException, IOException + { + synchronized (map) { + Class c = (Class) WeakMap.getValue(map.get(locationUrl)); + if (c != null) { + return c; + } + } + URL url = new URL(locationUrl); + Attributes attr = UnoClassLoader.getJarMainAttributes(url); + String name = attr == null + ? null : attr.getValue("RegistrationClassName"); + if (name == null) { + return null; + } + ClassLoader cl1 = RegistrationClassFinder.class.getClassLoader(); + ClassLoader cl2; + if (cl1 instanceof UnoClassLoader) { + cl2 = ((UnoClassLoader) cl1).getClassLoader(url, attr); + } else { + cl2 = URLClassLoader.newInstance(new URL[] { url }, cl1); + } + Class c = cl2.loadClass(name); + synchronized (map) { + Class c2 = (Class) WeakMap.getValue(map.get(locationUrl)); + if (c2 != null) { + return c2; + } + map.put(locationUrl, c); + } + return c; + } + + private RegistrationClassFinder() {} // do not instantiate + + private static final WeakMap map = new WeakMap(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/servicemanager/ServiceManager.java b/ridljar/com/sun/star/comp/servicemanager/ServiceManager.java new file mode 100644 index 0000000000..574983f1e2 --- /dev/null +++ b/ridljar/com/sun/star/comp/servicemanager/ServiceManager.java @@ -0,0 +1,662 @@ +/* -*- 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.comp.servicemanager; + +import java.util.ArrayList; +import java.util.Collections; + +import com.sun.star.container.XContentEnumerationAccess; +import com.sun.star.container.XEnumeration; +import com.sun.star.container.XSet; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +/** + * The ServiceManager class is an implementation of the + * ServiceManagerthe central class needed for implementing or using + * UNO components in Java. + * + *

The Methods queryInterface and isSame delegate + * calls to the implementing objects and are used instead of casts and identity + * comparisons.

+ * + * @see com.sun.star.lang.XMultiServiceFactory + * @see com.sun.star.container.XSet + * @see com.sun.star.container.XContentEnumerationAccess + * @see com.sun.star.lang.XComponent + * @see com.sun.star.lang.XServiceInfo + * @since UDK1.0 + */ +public class ServiceManager implements XMultiServiceFactory, + XMultiComponentFactory, + XSet, + XContentEnumerationAccess, + XComponent, + XServiceInfo +{ + private static final boolean DEBUG = false; + + private static final void DEBUG (String dbg) { + if (DEBUG) System.err.println( dbg ); + } + + private static com.sun.star.uno.Type UNO_TYPE = null; + + static String[] supportedServiceNames = { + "com.sun.star.lang.MultiServiceFactory", + "com.sun.star.lang.ServiceManager" + }; + + ArrayList eventListener; + java.util.HashMap factoriesByImplNames; + java.util.HashMap> factoriesByServiceNames; // keys: + + private com.sun.star.uno.XComponentContext m_xDefaultContext; + + /** + * Creates a new instance of the ServiceManager. + */ + public ServiceManager() { + eventListener = new ArrayList(); + factoriesByImplNames = new java.util.HashMap(); + factoriesByServiceNames = new java.util.HashMap>(); + m_xDefaultContext = null; + } + + public void setDefaultContext(XComponentContext context) { + m_xDefaultContext = context; + } + + /** + * Creates a new instance of a specified service. + * + *

Therefore the associated factory of the service is looked up and used + * to instantiate a new component.

+ * + * @param serviceSpecifier indicates the service or component name. + * @return newly created component. + * + * @see com.sun.star.lang.XMultiServiceFactory + */ + public java.lang.Object createInstance( String serviceSpecifier ) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return createInstanceWithContext( serviceSpecifier, m_xDefaultContext ); + } + + /** + * Creates a new instance of a specified service with the given parameters. + * + *

Therefore the associated factory of the service is looked up and used + * to instantiate a new component.

+ * + * @return newly created component. + * @param serviceSpecifier indicates the service or component name. + * @see com.sun.star.lang.XMultiServiceFactory + */ + public java.lang.Object createInstanceWithArguments( + String serviceSpecifier, Object[] args ) + throws com.sun.star.uno.Exception, com.sun.star.uno.RuntimeException + { + if (DEBUG) { + System.err.println("createInstanceWithArguments:" ); + + for (Object arg : args) { + System.err.print(" " + arg); + } + + System.err.println(); + } + + return createInstanceWithArgumentsAndContext( serviceSpecifier, args, m_xDefaultContext ); + } + + /** + * Look up the factory for a given service or implementation name. + * + *

First the requested service name is search in the list of available + * services. If it can not be found the name is looked up in the implementation + * list.

+ * + * @param serviceName indicates the service or implementation name. + * @return the factory of the service / implementation. + * + * @see com.sun.star.lang.XMultiServiceFactory + */ + private Object queryServiceFactory(String serviceName) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + DEBUG("queryServiceFactory for name " + serviceName ); + Object factory = null; + + if ( factoriesByServiceNames.containsKey( serviceName ) ) { + ArrayList availableFact = factoriesByServiceNames.get( serviceName ); + + DEBUG(""); + DEBUG("available factories for " + serviceName +" "+ availableFact); + DEBUG(""); + + if ( !availableFact.isEmpty() ) + factory = availableFact.get(availableFact.size()-1); + + } else // not found in list of services - now try the implementations + factory = factoriesByImplNames.get( serviceName ); // return null if none is available + + if (DEBUG) { + if (factory == null) System.err.println("service not registered"); + else + System.err.println("service found:" + factory + " " + UnoRuntime.queryInterface(XSingleServiceFactory.class, factory)); + } + + if (factory == null) + throw new com.sun.star.uno.Exception("Query for service factory for " + serviceName + " failed."); + + return factory; + } + + /** + * Supplies a list of all available services names. + * + * @return list of Strings of all service names. + * @see com.sun.star.container.XContentEnumerationAccess + */ + public String[] getAvailableServiceNames() + throws com.sun.star.uno.RuntimeException + { + try{ + return factoriesByServiceNames.keySet().toArray( + new String[ factoriesByServiceNames.size() ] ); + } catch(Exception ex) { + throw new com.sun.star.uno.RuntimeException(ex); + } + } + + // XMultiComponentFactory implementation + + /** Create a service instance with given context. + * + * @param rServiceSpecifier service name. + * @param xContext context. + * @return service instance. + */ + public java.lang.Object createInstanceWithContext( + String rServiceSpecifier, + com.sun.star.uno.XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object fac = queryServiceFactory( rServiceSpecifier ); + if (fac != null) + { + XSingleComponentFactory xCompFac = UnoRuntime.queryInterface( + XSingleComponentFactory.class, fac ); + if (xCompFac != null) + { + return xCompFac.createInstanceWithContext( xContext ); + } + else + { + XSingleServiceFactory xServiceFac = UnoRuntime.queryInterface( + XSingleServiceFactory.class, fac ); + if (xServiceFac != null) + { + if (DEBUG) + System.err.println( "### ignoring context raising service \"" + rServiceSpecifier + "\"!" ); + return xServiceFac.createInstance(); + } + else + { + throw new com.sun.star.uno.Exception( + "retrieved service factory object for \"" + rServiceSpecifier + + "\" does not export XSingleComponentFactory nor XSingleServiceFactory!" ); + } + } + } + return null; + } + /** + * Create a service instance with given context and arguments. + * + * @param rServiceSpecifier service name. + * @param rArguments arguments. + * @param xContext context. + * @return service instance. + */ + public java.lang.Object createInstanceWithArgumentsAndContext( + String rServiceSpecifier, + java.lang.Object[] rArguments, + com.sun.star.uno.XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object fac = queryServiceFactory( rServiceSpecifier ); + if (fac != null) + { + XSingleComponentFactory xCompFac = UnoRuntime.queryInterface( + XSingleComponentFactory.class, fac ); + if (xCompFac != null) + { + return xCompFac.createInstanceWithArgumentsAndContext( rArguments, xContext ); + } + else + { + XSingleServiceFactory xServiceFac = UnoRuntime.queryInterface( + XSingleServiceFactory.class, fac ); + if (xServiceFac != null) + { + if (DEBUG) + System.err.println( "### ignoring context raising service \"" + rServiceSpecifier + "\"!" ); + return xServiceFac.createInstanceWithArguments( rArguments ); + } + else + { + throw new com.sun.star.uno.Exception( + "retrieved service factory object for \"" + rServiceSpecifier + + "\" does not export XSingleComponentFactory nor XSingleServiceFactory!" ); + } + } + } + return null; + } + + /** + * Removes all listeners from the ServiceManager and clears the + * list of the services. + * + * @see com.sun.star.lang.XComponent + */ + public void dispose() + throws com.sun.star.uno.RuntimeException + { + if (eventListener != null) { + for (XEventListener listener : eventListener) { + listener.disposing(new com.sun.star.lang.EventObject(this)); + } + eventListener.clear(); + } + + factoriesByServiceNames.clear(); + factoriesByImplNames.clear(); + } + + /** + * Adds a new EventListener. + * + *

The listener is notified when a service is added (removed) to (from) + * the ServiceManager.

+ * + *

If the listener is already registered a + * com.sun.star.uno.RuntimeException will be thrown.

+ * + * @param xListener the new listener which should been added. + * @see com.sun.star.lang.XComponent + */ + public void addEventListener( XEventListener xListener ) + throws com.sun.star.uno.RuntimeException + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException("Listener must not be null"); + + if ( eventListener.contains(xListener) ) + throw new com.sun.star.uno.RuntimeException("Listener already registered."); + + eventListener.add(xListener); + } + + /** + * Removes a EventListener from the ServiceManager. + * + *

If the listener is not registered a com.sun.star.uno.RuntimeException + * will be thrown.

+ * + * @param xListener the new listener which should been removed. + * @see com.sun.star.lang.XComponent + */ + public void removeEventListener( XEventListener xListener ) + throws com.sun.star.uno.RuntimeException + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException("Listener must not be null"); + + if ( !eventListener.contains(xListener) ) + throw new com.sun.star.uno.RuntimeException("Listener is not registered."); + + eventListener.remove(xListener); + } + + /** + * Checks if a component is registered at the ServiceManager. + * + *

The given object argument must provide a XServiceInfo + * interface.

+ * + * @param object object which provides a XServiceInfo interface. + * @return true if the component is registered otherwise false. + * + * @see com.sun.star.container.XSet + * @see com.sun.star.lang.XServiceInfo + */ + public boolean has( Object object ) + throws com.sun.star.uno.RuntimeException + { + if (object == null) + throw new com.sun.star.uno.RuntimeException("The parameter must not been null"); + + XServiceInfo xServiceInfo = UnoRuntime.queryInterface(XServiceInfo.class, object); + + return xServiceInfo != null && UnoRuntime.areSame(factoriesByImplNames.get(xServiceInfo.getImplementationName()), object); + } + + /** + * Adds a SingleServiceFactory to the ServiceManager. + * + * @param object factory which should be added. + * @see com.sun.star.container.XSet + * @see com.sun.star.lang.XSingleServiceFactory + */ + public void insert( Object object ) + throws com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.ElementExistException, + com.sun.star.uno.RuntimeException + { + if (object == null) throw new com.sun.star.lang.IllegalArgumentException(); + + XServiceInfo xServiceInfo = + UnoRuntime.queryInterface(XServiceInfo.class, object); + + if (xServiceInfo == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object does not implement the XServiceInfo interface." + ); + + if ( factoriesByImplNames.containsKey( xServiceInfo.getImplementationName() ) ) { + throw new com.sun.star.container.ElementExistException( + xServiceInfo.getImplementationName() + " already registered" + ); + } + + DEBUG("add factory " + object.toString() + " for " + xServiceInfo.getImplementationName()); + factoriesByImplNames.put( xServiceInfo.getImplementationName(), object ); + + + String[] serviceNames = xServiceInfo.getSupportedServiceNames(); + ArrayList vec ; + + for (String serviceName : serviceNames) { + if (!factoriesByServiceNames.containsKey(serviceName)) { + DEBUG("> no registered services found under " + serviceName + ": adding..."); + factoriesByServiceNames.put(serviceName, new ArrayList()); + } + vec = factoriesByServiceNames.get(serviceName); + if (vec.contains( object )) { + System.err.println("The implementation " + xServiceInfo.getImplementationName() + + " already registered for the service " + serviceName + " - ignoring!"); + } else { + vec.add(object); + } + } + } + + /** + * Removes a SingleServiceFactory from the ServiceManager. + * + * @param object factory which should be removed. + * @see com.sun.star.container.XSet + * @see com.sun.star.lang.XSingleServiceFactory + */ + public void remove( Object object ) + throws com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.NoSuchElementException, + com.sun.star.uno.RuntimeException + { + if (object == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object must not be null." + ); + + XServiceInfo xServiceInfo = + UnoRuntime.queryInterface(XServiceInfo.class, object); + + if (xServiceInfo == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object does not implement the XServiceInfo interface." + ); + + XSingleServiceFactory xSingleServiceFactory = + UnoRuntime.queryInterface(XSingleServiceFactory.class, object); + + if (xSingleServiceFactory == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object does not implement the XSingleServiceFactory interface." + ); + + if ( factoriesByImplNames.remove( xServiceInfo.getImplementationName() ) == null ) + throw new com.sun.star.container.NoSuchElementException( + xServiceInfo.getImplementationName() + + " is not registered as an implementation." + ); + + String[] serviceNames = xServiceInfo.getSupportedServiceNames(); + + for (String serviceName : serviceNames) { + if (factoriesByServiceNames.containsKey(serviceName)) { + ArrayList vec = factoriesByServiceNames.get(serviceName); + if (!vec.remove(object)) { + System.err.println("The implementation " + xServiceInfo.getImplementationName() + + " is not registered for the service " + serviceName + " - ignoring!"); + } + // remove the vector if no implementations available for the service + if (vec.isEmpty()) { + factoriesByServiceNames.remove(serviceName); + } + } + } + } + + /** + * Provides an enumeration of all registered services. + * + * @return an enumeration of all available services. + * @see com.sun.star.container.XEnumerationAccess + */ + public XEnumeration createEnumeration() + throws com.sun.star.uno.RuntimeException + { + return new ServiceEnumerationImpl( factoriesByImplNames.values().iterator() ); + } + + /** + * Provides the UNO type of the ServiceManager + * + * @return the UNO type of the ServiceManager. + * @see com.sun.star.container.XElementAccess + * @see com.sun.star.uno.TypeClass + */ + public com.sun.star.uno.Type getElementType() + throws com.sun.star.uno.RuntimeException + { + if ( UNO_TYPE == null ) + UNO_TYPE = new com.sun.star.uno.Type(ServiceManager.class); + + return UNO_TYPE; + } + + /** + * Checks if the any components are registered. + * + * @return true - if the list of the registered components is not empty - otherwise false. + * @see com.sun.star.container.XElementAccess + */ + public boolean hasElements() { + return ! factoriesByImplNames.isEmpty(); + } + + /** + * Provides an enumeration of all factories for a specified service. + * + * @param serviceName name of the requested service. + * @return an enumeration for service name. + * @see com.sun.star.container.XContentEnumerationAccess + */ + public XEnumeration createContentEnumeration( String serviceName ) + throws com.sun.star.uno.RuntimeException + { + XEnumeration enumer ; + + ArrayList serviceList = factoriesByServiceNames.get(serviceName); + + if (serviceList != null) + enumer = new ServiceEnumerationImpl( serviceList.iterator() ); + else + enumer = new ServiceEnumerationImpl(); + + return enumer; + } + + /** + * Returns the implementation name of the ServiceManager component. + * + * @return the class name of the ServiceManager. + * @see com.sun.star.lang.XServiceInfo + */ + public String getImplementationName() + throws com.sun.star.uno.RuntimeException + { + return getClass().getName(); + } + + /** + * Checks if the ServiceManager supports a service. + * + * @param serviceName service name which should be checked. + * @return true if the service is supported - otherwise false. + * + * @see com.sun.star.lang.XServiceInfo + */ + public boolean supportsService( String serviceName ) + throws com.sun.star.uno.RuntimeException + { + for (String supportedServiceName : supportedServiceNames) { + if (supportedServiceName.equals(serviceName)) { + return true; + } + } + + return getImplementationName().equals(serviceName); + } + + /** + * Supplies list of all supported services. + * + * @return a list of all supported service names. + * @see com.sun.star.lang.XServiceInfo + */ + public String[] getSupportedServiceNames() + throws com.sun.star.uno.RuntimeException + { + return supportedServiceNames; + } + + /** + * The ServiceEnumerationImpl class provides an + * implementation of the @see com.sun.star.container.XEnumeration interface. + * + *

It is an inner wrapper for a java.util.Enumeration object.

+ * + * @see com.sun.star.lang.XSingleServiceFactory + * @see com.sun.star.lang.XServiceInfo + * @since UDK1.0 + */ + static class ServiceEnumerationImpl implements XEnumeration { + java.util.Iterator enumeration = null; + + /** + * Constructs a new empty instance. + */ + public ServiceEnumerationImpl() { + } + + /** + * Constructs a new instance with a given enumeration. + * + * @param enumer is the enumeration which should been wrapped. + * @see com.sun.star.container.XEnumeration + */ + public ServiceEnumerationImpl(java.util.Enumeration enumer) { + enumeration = Collections.list(enumer).iterator(); + } + + /** + * Constructs a new instance with a given enumeration. + * + * @param enumer is the enumeration which should been wrapped. + * @see com.sun.star.container.XEnumeration + */ + public ServiceEnumerationImpl(java.util.Iterator enumer) { + enumeration = enumer; + } + + /** + * Checks if the enumeration contains more elements. + * + * @return true if more elements are available - otherwise false. + * @see com.sun.star.container.XEnumeration + */ + public boolean hasMoreElements() + throws com.sun.star.uno.RuntimeException + { + return enumeration != null && enumeration.hasNext(); + + } + + /** + * Returns the next element of the enumeration. + * + *

If no further elements available a com.sun.star.container.NoSuchElementException + * exception will be thrown.

+ * + * @return the next element. + * @see com.sun.star.container.XEnumeration + */ + public Object nextElement() + throws com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException, + com.sun.star.uno.RuntimeException + { + if (enumeration == null) + throw new com.sun.star.container.NoSuchElementException(); + + try { + return enumeration.next(); + } catch (java.util.NoSuchElementException e) { + throw new com.sun.star.container.NoSuchElementException(e, e.toString()); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/urlresolver/UrlResolver.java b/ridljar/com/sun/star/comp/urlresolver/UrlResolver.java new file mode 100644 index 0000000000..7585dabf6d --- /dev/null +++ b/ridljar/com/sun/star/comp/urlresolver/UrlResolver.java @@ -0,0 +1,147 @@ +/* -*- 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.comp.urlresolver; + + +import com.sun.star.bridge.XBridge; +import com.sun.star.bridge.XBridgeFactory; +import com.sun.star.bridge.XUnoUrlResolver; + +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.IllegalArgumentException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.uno.UnoRuntime; + + +/** + * This component gives a factory for an UnoUrlResolver service. + * + * @see com.sun.star.bridge.XBridgeFactory + * @see com.sun.star.connection.Connector + * @since UDK1.0 + */ +public class UrlResolver { + public static class _UrlResolver implements XUnoUrlResolver { + private static final String __serviceName = "com.sun.star.bridge.UnoUrlResolver"; + + private final XMultiServiceFactory _xMultiServiceFactory; + + public _UrlResolver(XMultiServiceFactory xMultiServiceFactory) { + _xMultiServiceFactory = xMultiServiceFactory; + } + + public Object resolve(/*IN*/String dcp) throws NoConnectException, ConnectionSetupException, IllegalArgumentException, com.sun.star.uno.RuntimeException { + String conDcp ; + String protDcp ; + String rootOid ; + + if(dcp.indexOf(';') == -1) {// use old style + conDcp = dcp; + protDcp = "iiop"; + rootOid = "classic_uno"; + } + else { // new style + int index = dcp.indexOf(':'); + dcp = dcp.substring(index + 1).trim(); + + index = dcp.indexOf(';'); + conDcp = dcp.substring(0, index).trim(); + dcp = dcp.substring(index + 1).trim(); + + index = dcp.indexOf(';'); + protDcp = dcp.substring(0, index).trim(); + dcp = dcp.substring(index + 1).trim(); + + rootOid = dcp.trim().trim(); + } + + Object rootObject ; + XBridgeFactory xBridgeFactory ; + try { + xBridgeFactory = UnoRuntime.queryInterface(XBridgeFactory.class, + _xMultiServiceFactory.createInstance("com.sun.star.bridge.BridgeFactory")); + } catch (com.sun.star.uno.Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + XBridge xBridge = xBridgeFactory.getBridge(conDcp + ";" + protDcp); + + if(xBridge == null) { + Object connector ; + try { + connector = _xMultiServiceFactory.createInstance("com.sun.star.connection.Connector"); + } catch (com.sun.star.uno.Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + + XConnector connector_xConnector = UnoRuntime.queryInterface(XConnector.class, connector); + + // connect to the server + XConnection xConnection = connector_xConnector.connect(conDcp); + try { + xBridge = xBridgeFactory.createBridge(conDcp + ";" + protDcp, protDcp, xConnection, null); + } catch (com.sun.star.bridge.BridgeExistsException e) { + throw new com.sun.star.uno.RuntimeException(e); + } + } + rootObject = xBridge.getInstance(rootOid); + return rootObject; + } + } + + + /** + * Gives a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(UrlResolver.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(_UrlResolver.class, + _UrlResolver.__serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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 0000000000..b3a686f179 --- /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 XConnection interface + * and is uses by the PipeConnector and the PipeAcceptor. + * This class is not part of the provided api. + * + * 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 _aListeners; + protected boolean _bFirstRead; + + /** + * Constructs a new PipeConnection. + * + * @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(); + _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 0000000000..983eb32659 --- /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 XAcceptor interface. + * + *

The pipeAcceptor is a specialized component that uses TCP + * pipes for communication. The pipeAcceptor is generally used + * by the com.sun.star.connection.Acceptor service.

+ * + * @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. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.pipeAcceptor"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory 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. + * + *

This call blocks until a connection has been established.

+ * + *

The connection description has the following format: + * type*(key=value), + * where type should be pipe + * (ignoring case). Supported keys (ignoring case) currently are

+ *
+ *
host + *
The name or address of the accepting interface (defaults to + * 0, meaning any interface). + *
port + *
The TCP port number to accept on (defaults to 6001). + *
backlog + *
The maximum length of the acceptor's queue (defaults to + * 50). + *
tcpnodelay + *
A flag (0/1) enabling or disabling Nagle's + * algorithm on the resulting connection. + *
+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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 0000000000..192d350071 --- /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 XConnector interface. + * + *

The pipeConnector is a specialized component that uses TCP + * pipes for communication. The pipeConnector is generally + * used by the com.sun.star.connection.Connector service.

+ * + * @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. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName = "com.sun.star.connection.pipeConnector"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory 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. + * + *

The connection description has the following format: + * type*(key=value), + * where type should be pipe + * (ignoring case). Supported keys (ignoring case) currently are

+ *
+ *
host + *
The name or address of the server. Must be present. + *
port + *
The TCP port number of the server (defaults to 6001). + *
tcpnodelay + *
A flag (0/1) enabling or disabling Nagle's + * algorithm on the resulting connection. + *
+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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 0000000000..5a88d41baf --- /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 socketAcceptor and socketConnector. + * + *

FIXME: Once those classes have been moved from jurt to + * javaunohelper, they should use + * com.sun.star.lib.uno.helper.UnoUrl either instead of this class + * or underneath this class.

+ */ +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 0000000000..a906496f2c --- /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 XConnection interface + * and is uses by the SocketConnector and the SocketAcceptor. + * + *

This class is not part of the provided api.

+ * + * @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 _listeners; + protected boolean _firstRead; + + /** + * Constructs a new SocketConnection. + * + * @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(); + _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 0000000000..4000a1d0a4 --- /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 XAcceptor interface. + * + *

The socketAcceptor is a specialized component that uses TCP + * sockets for communication. The socketAcceptor is generally used + * by the com.sun.star.connection.Acceptor service.

+ * + * @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. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.socketAcceptor"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory 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. + * + *

This call blocks until a connection has been established.

+ * + *

The connection description has the following format: + * type*(key=value), + * where type should be socket + * (ignoring case). Supported keys (ignoring case) currently are

+ *
+ *
host + *
The name or address of the accepting interface (defaults to + * 0, meaning any interface). + *
port + *
The TCP port number to accept on (defaults to 6001). + *
backlog + *
The maximum length of the acceptor's queue (defaults to + * 50). + *
tcpnodelay + *
A flag (0/1) enabling or disabling Nagle's + * algorithm on the resulting connection. + *
+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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 0000000000..c9a15d1f5d --- /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 XConnector interface. + * + *

The socketConnector is a specialized component that uses TCP + * sockets for communication. The socketConnector is generally + * used by the com.sun.star.connection.Connector service.

+ * + * @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. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.socketConnector"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory 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. + * + *

The connection description has the following format: + * type*(key=value), + * where type should be socket + * (ignoring case). Supported keys (ignoring case) currently are

+ *
+ *
host + *
The name or address of the server. Must be present. + *
port + *
The TCP port number of the server (defaults to 6001). + *
tcpnodelay + *
A flag (0/1) enabling or disabling Nagle's + * algorithm on the resulting connection. + *
+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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/connections/websocket/ConnectionDescriptor.java b/ridljar/com/sun/star/lib/connections/websocket/ConnectionDescriptor.java new file mode 100644 index 0000000000..439a525517 --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/websocket/ConnectionDescriptor.java @@ -0,0 +1,60 @@ +/* -*- 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.websocket; + +import java.util.Arrays; +import java.util.Iterator; + +/** + * Helper class for websocketConnector. + */ +final class ConnectionDescriptor { + public ConnectionDescriptor(String description) + throws com.sun.star.lang.IllegalArgumentException { + Iterator descriptionParts = Arrays.stream(description.split(",")).iterator(); + descriptionParts + .next(); // skip over the first part as it's the protocol not a real parameter + while (descriptionParts.hasNext()) + { + String parameter = descriptionParts.next(); + String[] pair = parameter.split("=", 2); + + if (pair.length != 2) + { + throw new com.sun.star.lang.IllegalArgumentException( + String.format("parameter %s lacks '='", parameter)); + } + + String key = pair[0]; + String value = pair[1]; + if (key.equalsIgnoreCase("url")) { + url = value; + } + } + } + + public String getURL() { + return url; + } + + private String url = null; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/websocket/WebsocketConnection.java b/ridljar/com/sun/star/lib/connections/websocket/WebsocketConnection.java new file mode 100644 index 0000000000..87a8604c7b --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/websocket/WebsocketConnection.java @@ -0,0 +1,326 @@ +/* -*- 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.websocket; + + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.ProtocolException; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.swing.text.html.HTMLDocument.Iterator; + +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnectionBroadcaster; +import com.sun.star.io.XStreamListener; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +/** + * The WebsocketConnection implements the XConnection interface + * and is uses by the WebsocketConnector. + * + *

This class is not part of the provided api.

+ * + * @see com.sun.star.lib.connections.socket.socketAcceptor + * @see com.sun.star.lib.connections.socket.socketConnector + * @see com.sun.star.connection.XConnection + */ +public class WebsocketConnection extends WebSocketClient implements XConnection, XConnectionBroadcaster { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + static final byte[] outgoingPrefix = { 'u', 'r', 'p', ' ' }; + + protected String _description; + protected InputStream _inputStream; + protected OutputStream _inputStreamWriter; + protected ByteArrayOutputStream _outputStream; + + protected ArrayList _listeners; + + /** + * Constructs a new WebsocketConnection. + * + * @param description the description of the connection. + * @param desc the websocket ConnectionDescriptor containing information such as the websocket URL + */ + public WebsocketConnection(String description, ConnectionDescriptor desc) throws IOException, URISyntaxException, InterruptedException { + super(new URI(desc.getURL())); + + if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated " + description + " " + desc); + + _description = description; + + PipedOutputStream inputStreamWriter = new PipedOutputStream(); + PipedInputStream inputPipe = new PipedInputStream(inputStreamWriter); + + _inputStream = new BufferedInputStream(inputPipe); + _inputStreamWriter = inputStreamWriter; + _outputStream = new ByteArrayOutputStream(); + + _listeners = new ArrayList(); + + connectBlocking(); + } + + 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 { + + String errMessage = null; + + int read_bytes = 0; + bytes[0] = new byte[nBytesToRead]; + + try { + _inputStreamWriter.flush(); + + 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(String.format("##### %s - read %s bytes of %s requested", getClass().getName(), Integer.toString(read_bytes), Integer.toString(nBytesToRead))); + + 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(String.format("##### %s - wrote %s bytes", getClass().getName(), Integer.toString(aData.length))); + } + + /** + * Sends the data over the websocket to whatever is on the other side. + * + * **NOTE**: unlike with genuine streams, without flushing the data is + * never sent + * + * @see com.sun.star.connection.XConnection#flush + */ + public void flush() throws com.sun.star.io.IOException, + com.sun.star.uno.RuntimeException { + + byte[] accumulatedBytes = _outputStream.toByteArray(); + _outputStream.reset(); + + byte[] outputBytes = new byte[accumulatedBytes.length + outgoingPrefix.length]; + System.arraycopy(outgoingPrefix, 0, outputBytes, 0, outgoingPrefix.length); + System.arraycopy(accumulatedBytes, 0, outputBytes, outgoingPrefix.length, accumulatedBytes.length); + send(outputBytes); + + if (DEBUG) + System.err.println(String.format("##### %s - flushed", getClass().getName())); + } + + /** + * Closes the connection. + * + * @see com.sun.star.connection.XConnection#close + */ + public void close() throws com.sun.star.uno.RuntimeException { + if (DEBUG) System.err.println("##### " + getClass().getName() + " - socket closed"); + super.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; + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + notifyListeners_open(); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + notifyListeners_close(); + } + + @Override + public void onMessage(String message) { + String[] messageParts = message.split(": ", 2); + if (messageParts.length != 2) + { + notifyListeners_error(new com.sun.star.uno.Exception(new ProtocolException(String.format("Received URP/WS message (%s) without a type specifier. Messages must be proceeded by 'urp: '", message)))); + return; + } + + String messageType = messageParts[0]; + + if (!messageType.equals("urp")) + { + if (DEBUG) System.err.println(String.format("##### %s - received %s message but that is not URP", getClass().getName(), messageType)); + return; + } + + byte[] messageBytes = messageParts[1].getBytes(StandardCharsets.UTF_8); + + try { + _inputStreamWriter.write(messageBytes); + _inputStreamWriter.flush(); + } catch (IOException e) { + notifyListeners_error(new com.sun.star.uno.Exception(e)); + return; + } + + if (DEBUG) System.err.println(String.format("##### %s - received %s chars", getClass().getName(), Integer.toString(messageBytes.length))); + } + + @Override + public void onMessage(ByteBuffer message) { + byte[] prefixedMessageBytes = message.array(); + + StringBuffer messageTypeBuf = new StringBuffer(); + boolean hasType = false; + int i; + for (i = 0; i < prefixedMessageBytes.length - 1; i++) { + if (prefixedMessageBytes[i] == ':' && prefixedMessageBytes[i+1] == ' ') { + hasType = true; + break; // The type ends with ": ", so if we find this sequence we found the end of our type + } + messageTypeBuf.append((char)prefixedMessageBytes[i]); + } + + if(!hasType) { + notifyListeners_error(new com.sun.star.uno.Exception(new ProtocolException(String.format("Received URP/WS message (%s) without a type specifier. Binary messages must be proceeded by 'urp: '", message)))); + return; + } + + String messageType = messageTypeBuf.toString(); + + int messageStartIndex = i + 2; + + if (!messageType.equals("urp")) { + if (DEBUG) System.err.println(String.format("##### %s - received %s binary message but that is not URP", getClass().getName(), messageType)); + return; + } + + byte[] messageBytes = Arrays.copyOfRange(prefixedMessageBytes, messageStartIndex, prefixedMessageBytes.length); + + try { + _inputStreamWriter.write(messageBytes); + _inputStreamWriter.flush(); + } catch (IOException e) { + notifyListeners_error(new com.sun.star.uno.Exception(e)); + return; + } + + if (DEBUG) System.err.println(String.format("##### %s - received %s bytes", getClass().getName(), Integer.toString(prefixedMessageBytes.length))); + } + + @Override + public void onError(Exception ex) { + notifyListeners_error(new com.sun.star.uno.Exception(ex)); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/websocket/websocketConnector.java b/ridljar/com/sun/star/lib/connections/websocket/websocketConnector.java new file mode 100644 index 0000000000..a40bb0093c --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/websocket/websocketConnector.java @@ -0,0 +1,137 @@ +/* -*- 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.websocket; + +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.URISyntaxException; +import java.net.UnknownHostException; + +/** + * A component that implements the XConnector interface. + * + *

The websocketConnector is a specialized component that uses + * websockets for communication. The websocketConnector is generally + * used by the com.sun.star.connection.Connector service.

+ * + * @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 + */ +public final class websocketConnector implements XConnector { + /** + * The name of the service. + * + *

The JavaLoader accesses this through reflection.

+ * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.websocketConnector"; + + /** + * Returns a factory for creating the service. + * + *

This method is called by the JavaLoader.

+ * + * @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 XSingleServiceFactory for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(websocketConnector.class.getName()) + ? FactoryHelper.getServiceFactory(websocketConnector.class, + __serviceName, multiFactory, + regKey) + : null; + } + + /** + * Connects via the described websocket to a waiting server. + * + *

The connection description has the following format: + * type*(key=value), + * where type should be websocket + * (ignoring case). Supported keys (ignoring case) currently are

+ *
+ *
url + *
The URL the websocket server is listening on, starting with + * either ws:// or wss:// + *
+ * + * @param connectionDescription the description of the connection. + * @return an XConnection 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 to the socket"); + + ConnectionDescriptor desc; + try { + desc = new ConnectionDescriptor(connectionDescription); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new ConnectionSetupException(e); + } + + WebsocketConnection websocket = null; + try { + websocket = new WebsocketConnection(connectionDescription, desc); + connected = websocket.isOpen(); + } catch (IOException e) { + throw new ConnectionSetupException(e); + } catch (URISyntaxException e) { + throw new ConnectionSetupException(e); + } catch (InterruptedException e) { + throw new ConnectionSetupException(e); + } + + if (websocket == null || !connected) + throw new ConnectionSetupException("Could not connect to the server. Is it up?"); + + return websocket; + } + + 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 0000000000..7d3612758f --- /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. + * + *

Currently, this interface is used internally by + * com.sun.star.lib.uno.environments.java.java_environment to + * distinguish between proxies and local objects. Any proxy object that shall + * be registered at the java_environment must implement this marker + * interface.

+ */ +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 0000000000..2d3e9a8480 --- /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 0000000000..dd634e7713 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 InputStreamToInputXStreamAdapter wraps the + Java InputStream object into a + UNO XInputStream object. + This allows users to access an InputStream + as if it were an XInputStream. + */ +public final class InputStreamToXInputStreamAdapter implements XInputStream { + + /** + * Internal store to the InputStream + */ + private final InputStream iIn; + + /** + * Constructor. + * + * @param in The XInputStream to be + * accessed as an InputStream. + */ + 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; + int totalBytesRead = 0; + if (b[0] == null || b[0].length < len) { + b[0] = new byte[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. + while ((len > 0) && ((bytesRead = iIn.read(b[0], totalBytesRead, len)) > 0)) { + totalBytesRead += (int)bytesRead; + len -= (int)bytesRead; + } + if (totalBytesRead < b[0].length) { + byte[] out = new byte[totalBytesRead]; + System.arraycopy(b[0], 0, out, 0, totalBytesRead); + b[0] = out; + } + return totalBytesRead; + } 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 (b[0] == null || b[0].length < len) { + b[0] = new byte[len]; + } + 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 < b[0].length) { + int outSize = bytesRead > 0 ? (int)bytesRead : 0; + byte[] out = new byte[outSize]; + System.arraycopy(b[0], 0, out, 0, outSize); + b[0] = out; + } + 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); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ 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 0000000000..2842e3df05 --- /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 OutputStreamToXOutputStreamAdapter wraps + a UNO XOutputStream into a Java OutputStream + object in a Java. This allows users to access an OutputStream + as if it were an XOutputStream. + */ +public final class OutputStreamToXOutputStreamAdapter implements XOutputStream { + + /** + * Internal handle to the OutputStream + */ + OutputStream iOut; + + /** + * Constructor. + * + * @param out The XOutputStream to be + * accessed as an OutputStream. + */ + 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 0000000000..25f1798fba --- /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 XInputStreamToInputStreamAdapter wraps + * the UNO XInputStream object in a Java + * InputStream. This allows users to access + * an XInputStream as if it were an + * InputStream. + */ +public final class XInputStreamToInputStreamAdapter extends InputStream { + + /** + * Internal handle to the XInputStream + */ + private final XInputStream xin; + + /** + * Constructor. + * + * @param in The XInputStream to be + * accessed as an InputStream. + */ + 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 + * XInputStreamToInputStreamAdapter 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 0000000000..48871277a7 --- /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 0000000000..8104cf42fa --- /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 XOutputStreamToOutputStreamAdapter wraps + * the UNO XOutputStream object in a Java + * OutputStream. This allows users to access + * an XOutputStream as if it were an + * OutputStream. + */ +public final class XOutputStreamToOutputStreamAdapter extends OutputStream { + + /** + * Internal handle to the XInputStream + */ + XOutputStream xout; + + /** + * Constructor. + * + * @param out The XOutputStream to be + * accessed as an OutputStream. + */ + 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 0000000000..0a724f0593 --- /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 0000000000..1b38489835 --- /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 java_remote_bridge. + * + *

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.

+ */ +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 0000000000..d5246bf26c --- /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 ProxyFactory (which + * receive requests in the form of method calls) and + * java_remote_bridge (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 0000000000..8d660e88ca --- /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 0000000000..ac198f8fdd --- /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 0000000000..392e031e30 --- /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. + * + *

Therefore various interfaces are implemented.

+ * + *

The protocol to used is passed by name, the bridge + * then looks for it under com.sun.star.lib.uno.protocols.

+ * + * @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 _listeners = new ArrayList(); + + 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). + * + *

When this bridge is disposed, all remaining ref holder entries are + * released.

+ */ + 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> refHolders = new HashMap>(); + // from OID (String) to LinkedList of RefHolder + + private boolean hasRefHolder(String oid, Type type) { + synchronized (refHolders) { + LinkedList 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 l = refHolders.get(oid); + if (l == null) { + l = new LinkedList(); + refHolders.put(oid, l); + } + boolean found = false; + for (Iterator 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 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>> i1 = refHolders.entrySet().iterator(); i1.hasNext();) + { + Map.Entry> e = i1.next(); + String oid = e.getKey(); + LinkedList l = e.getValue(); + for (Iterator 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 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. + *

This method is not part of the provided api + * and should only be used by the UNO runtime.

+ * + * @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. + * + *

If the life count drops to zero, the bridge disposes itself.

+ * + * @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 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 this: + private final ArrayList disposeListeners = new ArrayList(); +} + +/* 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 0000000000..89e4b10880 --- /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. + * + *

The java_environment implements the IEnvironment interface + * defined in the uno runtime.

+ * + * @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. + * + *

This method should be part of IEnvironment. It is called + * from com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge.dispose.

+ */ + 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 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 level2map = + new HashMap(); + } + + private static final class Level2Entry extends WeakReference { + public Level2Entry( + String oid, Type type, Object object, ReferenceQueue 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 level1map = + new HashMap(); + private final ReferenceQueue queue = new ReferenceQueue(); + } + + 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 0000000000..c2ecbf9a09 --- /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. + * + *

A class implementing a given protocol prot must be named + * com.sun.star.lib.uno.protocols.prot.prot + * and must have a public constructor that takes four arguments: The first + * argument of type com.sun.star.uno.IBridge must not be null. The + * second argument of type String represents any attributes; it may + * be null if there are no attributes. The third argument of type + * java.io.InputStream must not be null. The fourth argument of + * type java.io.OutputStream must not be null.

+ */ +public interface IProtocol { + /** + * Initializes the connection. + * + *

This method must be called exactly once, after the + * readMessage loop has already been established.

+ */ + void init() throws IOException; + + void terminate(); + + /** + * Reads a request or reply message. + * + *

Access to this method from multiple threads must be properly + * synchronized.

+ * + * @return a non-null message; if the input stream is exhausted, a + * java.io.IOException 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 type, or either + * "queryInterface" or "release"). + * @param tid a non-null TID. + * @param arguments a list of UNO arguments compatible with the given + * type and function; may be null to represent + * an empty list. + * @return true 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 true if the reply corresponds to a raised + * exception. + * @param tid a non-null TID. + * @param result if exception is true, a non-null + * UNO exception; otherwise, a UNO return value, which may be null to + * represent a VOID 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 0000000000..e39ae3d4d5 --- /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 true 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 0000000000..1de31ad04c --- /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. + * + *

The function exists for performance.

+ * + * @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. + * + *

The function exists for performance.

+ * + * @see #attach() + * @see #detach() + */ + void detach( Object handle, ThreadId id ); + + /** + * Lets this thread enter the thread pool. + * + *

This thread then executes all jobs put via putJob until + * a reply job arrives.

+ * + * @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. + * + *

This thread then executes all jobs put via putJob until + * a reply job arrives.

+ * + * @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 + * DisposedException with the given Throwable 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 0000000000..332306be0e --- /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 0000000000..181a3e17e4 --- /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 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 threadIdMap = new WeakHashMap(); + private final HashMap jobQueues = new HashMap(); +} + +/* 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 0000000000..8ec4492c4c --- /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 queryInterface call. + * + * @return the result of the call (should be an Any). + */ + 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 0000000000..b71525ba5d --- /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 JobQueue implements a queue for jobs. + * + *

For every jobs thread id exists a job queue which is registered + * at the ThreadPool.

+ * + *

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 putjob) into the async queue, which is only + * known by the sync queue.

+ * + * @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 jobList = new ArrayList(); + + 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.:" + _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 0000000000..b34295ae33 --- /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. + * + *

Valid for all kinds of messages.

+ * + * @return the (non-null) thread ID. + */ + public final ThreadId getThreadId() { + return threadId; + } + + /** + * Returns whether the message is a request or a reply. + * + *

Valid for all kinds of messages.

+ * + * @return true for a request, false for a reply. + */ + public final boolean isRequest() { + return request; + } + + /** + * Returns the object ID of a request message. + * + *

Valid only for request messages.

+ * + * @return the (non-null) object ID for a request, + * null for a reply. + */ + public final String getObjectId() { + return objectId; + } + + /** + * Returns the type of a request message. + * + *

Valid only for request messages.

+ * + * @return the (non-null) type for a request, null + * for a reply. + */ + public final TypeDescription getType() { + return type; + } + + /** + * Returns the method description of a request message. + * + *

Valid only for request messages. The returned + * MethodDescription is consistent with the type of the + * message.

+ * + * @return the (non-null) method description for a request, + * null for a reply. + */ + public final MethodDescription getMethod() { + return method; + } + + /** + * Returns whether the request message is synchronous. + * + *

Valid only for request messages.

+ * + * @return true for a synchronous request, false + * for an asynchronous request or a reply. + */ + public final boolean isSynchronous() { + return synchronous; + } + + /** + * Returns the current context of a request message. + * + *

Valid only for request messages.

+ * + * @return the current context (which may be null) for a + * request, null for a reply. + */ + public XCurrentContext getCurrentContext() { + return currentContext; + } + + /** + * Returns whether the reply message represents abnormal termination. + * + *

Valid only for reply messages.

+ * + * @return true for a reply that represents abnormal + * termination, false for a reply that represents normal + * termination or a request. + */ + public final boolean isAbnormalTermination() { + return abnormalTermination; + } + + /** + * Returns the result of a reply message. + * + *

Valid only for reply messages.

+ * + * @return any (possibly null) return value for a reply that + * represents normal termination, the (non-null) exception for + * a reply that represents abnormal termination, null for a + * request. + */ + public final Object getResult() { + return result; + } + + /** + * Returns the arguments of a message. + * + *

Valid only for request messages and reply messages that represent + * normal termination. Any returned array must not be modified.

+ * + * @return the in and in– { + * }out arguments for a request (possibly + * null for a parameterless function), the out and in– { + * }out + * arguments for a reply that represents normal termination (possibly + * null for a parameterless function), null 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 0000000000..f77bbb466d --- /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 0000000000..f8dacc78cc --- /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 true if this object is the same as the obj argument; + * false 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 0000000000..2014f51674 --- /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. + * + *

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.

+ * + *

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 org.openoffice.native (to be set by the + * native code that starts the JVM) to determine which implementation + * to use.

+ */ +public final class ThreadPoolManager { + /** + * Creates a thread pool instance. + * + * @return a new thread pool instance; will never be null. + */ + 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 false 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 0000000000..09259c8cb0 --- /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 0000000000..d886ef7020 --- /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 0000000000..056d9549d5 --- /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. + +

+ Note: This factory implementation does not support lang.XSingleServiceFactory. +

+*/ +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 0000000000..e858ced81f --- /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 + *
+ * 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.
+ * 
+ * + * 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 ArrayList instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an ArrayList 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 ArrayList 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 true (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 + * (index < 0 || index > size()). + */ + 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 (index + * < 0 || index > size()). + * @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(index + * < 0 || index > size()). + * @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; itrue if this list contains the specified element. + * + * @param elem element whose presence in this List is to be tested. + * @return true 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 (index + * < 0 || index >= size()). + */ + 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 equals method. + * + * @param elem an object. + * @return the index of the first occurrence of the argument in this + * list; returns -1 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 true if this list has no elements; + * false 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 ArrayList instance. The contained + * references are copied but the objects not. + * + * @return a clone of this List 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 (index + * < 0 || index >= size()). + */ + 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 + * (index < 0 || index >= size()). + */ + 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.

+ * + * 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 + * null. This is useful in determining the length of the list + * only if the caller knows that the list does not contain any + * null 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 0000000000..9b061f81c2 --- /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 map= new HashMap(); + + /** 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 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 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 it= null; + synchronized(this) + { + it= map.values().iterator(); + } + while (it.hasNext() ) { + it.next().disposeAndClear(evt); + } + } + + synchronized public void clear() + { + Iterator 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 0000000000..dec51b9b08 --- /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

+ * Properties have to be registered by one of the registerProperty methods. They take among other + * arguments an Object named id 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.

+ * Example: + *

+ *  public class Foo extends PropertySet
+ *  {
+ *      protected int intProp;
+ *
+ *      public Foo()
+ *      {
+ *          registerProperty("PropertyA", 0, new Type(int.class), (short)0, "intProp");
+ *      }
+ *  }
+ *
+ *  
+ */ +public class PropertySet extends ComponentBase implements XPropertySet, XFastPropertySet, +XMultiPropertySet +{ + private HashMap _nameToPropertyMap; + private HashMap _handleToPropertyMap; + private HashMap _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 id with it. + * id 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 id 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.
+ * 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 propertyName. + */ + protected Property getProperty(String propertyName) + { + return _nameToPropertyMap.get(propertyName); + } + + /** Returns the Property object with a handle (Property.Handle) as specified by the argument + * nHandle. 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 nHandle + */ + 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 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 id 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(); + _handleToPropertyMap= new HashMap(); + _propertyToIdMap= new HashMap(); + } + + /** 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: + *
    + *
  • java.lang.Boolean
  • + *
  • java.lang.Character
  • + *
  • java.lang.Byte
  • + *
  • java.lang.Short
  • + *
  • java.lang.Integer
  • + *
  • java.lang.Long
  • + *
  • java.lang.Float
  • + *
  • java.lang.Double
  • + *
  • String
  • + *
  • com.sun.star.uno.Type
  • + *
  • objects which implement UNO interfaces
  • + *
  • arrays which contain elements of the types above
  • + *
  • com.sun.star.uno.Any containing an instance of one of the above types
  • + *
+ * + * 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 value 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 + * value = 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 value = 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. + *

+ * 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: + *

+     *  set.setPropertyValue( "PropA", Byte.valueOf( (byte)111));
+     *  
+ * 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 newVal. 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. + *

+ * The method handles Any arguments the same as Object arguments. That is, the setVal 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 setVal 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 setVal 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 newVal + * returns the XInterface. + * + * If a member is a UNO interface, then setVal is queried for this interface and the result is returned. + * If setVal is null then newVal will be null too after return. + *

+ * If a property value is stored using a primitive type the out-parameters + * curVal and newVal 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. newVal 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= Character.valueOf(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= Float.valueOf(AnyConverter.toFloat(obj)); + else if (cl.equals(double.class)) + retVal= Double.valueOf(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= Character.valueOf(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= Float.valueOf(AnyConverter.toFloat(obj)); + else if (cl.equals(Double.class)) + retVal= Double.valueOf(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}.
+ * 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 bVetoable 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 0000000000..0c050d6769 --- /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. + +

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) + com.sun.star.beans.XPropertySet, + com.sun.star.beans.XFastPropertySet, and + com.sun.star.beans.XPropertyAccess to it.

+ +

Client code should not use the monitors associated with instances of this + class, as they are used for internal purposes.

+ + @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 + com.sun.star.reflection.theCoreReflection and + com.sun.star.reflection.theTypeDescriptionManager singletons + + @param object the client UNO object into which this instance is mixed in; + must not be null, and must support the given type + + @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 + com.sun.star.beans.XPropertySet.getPropertySetInfo, + com.sun.star.beans.XPropertySet.addPropertyChangeListener, + com.sun.star.beans.XPropertySet.removePropertyChangeListener, + com.sun.star.beans.XPropertySet.addVetoableChangeListener, + and com.sun.star.beans.XPropertySet.removeVetoableChangeListener; 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 + absentOptional 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 + com.sun.star.beans.UnknownPropertyException), 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 + com.sun.star.beans.UnknownPropertyException) but is not + contained in the given absentOptional, then it will be + visible via + com.sun.star.beans.XPropertySet.getPropertySetInfo as a + com.sun.star.beans.Property with a set + com.sun.star.beans.PropertyAttribute.OPTIONAL. If the given + object does not implement + com.sun.star.beans.XPropertySet, then the given + absentOptional 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 map = new HashMap(); + ArrayList handleNames = new ArrayList(); + initProperties(ifc, map, handleNames, new HashSet()); + properties = map; + handleMap = handleNames.toArray(new String[handleNames.size()]); + } + + /** + A method used by clients when implementing UNO interface type attribute + setter functions. + +

First, this method checks whether this instance has already been + disposed (see {@link #dispose}), and throws a + com.sun.star.beans.DisposedException if applicable. For a + constrained attribute (whose setter can explicitly raise + com.sun.star.beans.PropertyVetoException), this method + notifies any com.sun.star.beans.XVetoableChangeListeners. + For a bound attribute, this method modifies the passed-in + bound so that it can afterwards be used to notify any + com.sun.star.beans.XPropertyChangeListeners. This method + should be called before storing the new attribute value, and + bound.notifyListeners() should be called exactly once after + storing the new attribute value (in case the attribute is bound; + otherwise, calling bound.notifyListeners() is ignored). + Furthermore, bound.notifyListeners() and this method have to + be called from the same thread.

+ + @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 + com.sun.star.beans.PropertyChangeEvent.OldValue, which is + rather useless, anyway (see “Using the Observer Pattern” in + + OpenOffice.org Coding Guidelines). If the attribute + that is going to be set is neither bound nor constrained, or if + com.sun.star.beans.PropertyChangeEvent.OldValue 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 + com.sun.star.beans.PropertyChangeEvent.NewValue, which is + rather useless, anyway (see “Using the Observer Pattern” in + + OpenOffice.org Coding Guidelines), unless 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 + com.sun.star.beans.PropertyChangeEvent.NewValue 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 specificVeto = null; + ArrayList unspecificVeto = null; + synchronized (this) { + if (disposed) { + throw new DisposedException("disposed", object); + } + if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { + ArrayList o = vetoListeners.get(propertyName); + if (o != null) { + specificVeto = new ArrayList(o); + } + o = vetoListeners.get(""); + if (o != null) { + unspecificVeto = new ArrayList(o); + } + } + if ((p.Attributes & PropertyAttribute.BOUND) != 0) { + // assert bound != null; + ArrayList o = boundListeners.get(propertyName); + if (o != null) { + bound.specificListeners = new ArrayList(o); + } + o = boundListeners.get(""); + if (o != null) { + bound.unspecificListeners = new ArrayList(o); + } + } + } + if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { + PropertyChangeEvent event = new PropertyChangeEvent( + object, propertyName, false, p.Handle, oldValue, newValue); + if (specificVeto != null) { + for (Iterator i = specificVeto.iterator(); i.hasNext();) { + try { + i.next().vetoableChange(event); + } catch (DisposedException e) {} + } + } + if (unspecificVeto != null) { + for (Iterator 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)}. + +

This method is useful for attributes that are not constrained.

+ + @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. + +

See com.sun.star.lang.XComponent for the general concept + of disposing UNO objects. On the first call to this method, all + registered listeners + (com.sun.star.beans.XPropertyChangeListeners and + com.sun.star.beans.XVetoableChangeListeners) are notified of + the disposing source. Any subsequent calls to this method are + ignored.

+ */ + public void dispose() { + HashMap> bound; + HashMap> veto; + synchronized (this) { + bound = boundListeners; + boundListeners = null; + veto = vetoListeners; + vetoListeners = null; + disposed = true; + } + EventObject event = new EventObject(object); + if (bound != null) { + for (Iterator> i = bound.values().iterator(); i.hasNext();) { + for (Iterator j = i.next().iterator(); j.hasNext();) + { + j.next().disposing(event); + } + } + } + if (veto != null) { + for (Iterator> i = veto.values().iterator(); i.hasNext();) { + for (Iterator j = i.next().iterator(); j.hasNext();) + { + j.next().disposing(event); + } + } + } + } + + /** + Implements + com.sun.star.beans.XPropertySet.getPropertySetInfo. + @return See com.sun.star.beans.XPropertySet + */ + public XPropertySetInfo getPropertySetInfo() { + return new Info(properties); + } + + /** + Implements com.sun.star.beans.XPropertySet.setPropertyValue. + @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 com.sun.star.beans.XPropertySet.getPropertyValue. + @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 + com.sun.star.beans.XPropertySet.addPropertyChangeListener. + +

If a listener is added more than once, it will receive all relevant + notifications multiple times.

+ + @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 v = boundListeners.get(propertyName); + if (v == null) { + v = new ArrayList(); + boundListeners.put(propertyName, v); + } + v.add(listener); + } + } + if (disp) { + listener.disposing(new EventObject(object)); + } + } + + /** + Implements + com.sun.star.beans.XPropertySet.removePropertyChangeListener. + + @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 v = boundListeners.get(propertyName); + if (v != null) { + v.remove(listener); + } + } + } + } + + /** + Implements + com.sun.star.beans.XPropertySet.addVetoableChangeListener. + +

If a listener is added more than once, it will receive all relevant + notifications multiple times.

+ + @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 v = vetoListeners.get(propertyName); + if (v == null) { + v = new ArrayList(); + vetoListeners.put(propertyName, v); + } + v.add(listener); + } + } + if (disp) { + listener.disposing(new EventObject(object)); + } + } + + /** + Implements + com.sun.star.beans.XPropertySet.removeVetoableChangeListener. + + @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 v = vetoListeners.get(propertyName); + if (v != null) { + v.remove(listener); + } + } + } + } + + /** + Implements + com.sun.star.beans.XFastPropertySet.setFastPropertyValue. + + @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 + com.sun.star.beans.XFastPropertySet.getFastPropertyValue. + + @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 + com.sun.star.beans.XPropertyAccess.getPropertyValues. + + @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 + com.sun.star.beans.XPropertyAccess.setPropertyValues. + + @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 + com.sun.star.beans.XPropertyChangeListeners. + + @see #prepareSet(String, Object, Object, + PropertySetMixin.BoundListeners) + */ + public void notifyListeners() { + if (specificListeners != null) { + for (Iterator i = specificListeners.iterator(); i.hasNext();) { + try { + i.next().propertyChange(event); + } catch (DisposedException e) {} + } + } + if (unspecificListeners != null) { + for (Iterator i = unspecificListeners.iterator(); i.hasNext();) + { + try { + i.next().propertyChange(event); + } catch (DisposedException e) {} + } + } + } + + private ArrayList specificListeners = null; + private ArrayList unspecificListeners = null; + private PropertyChangeEvent event = null; + } + + private XIdlClass getReflection(String typeName) { + return theCoreReflection.get(context).forName(typeName); + } + + private void initProperties( + XTypeDescription type, HashMap map, ArrayList handleNames, HashSet 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 properties) { + this.properties = properties; + } + + public Property[] getProperties() { + ArrayList al = new ArrayList(properties.size()); + for (Iterator 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 properties; + } + + private final XComponentContext context; + private final XInterface object; + private final Type type; + private final String[] absentOptional; + private final XIdlClass idlClass; + private final Map properties; // from String to Property + private final String[] handleMap; + + private HashMap> boundListeners + = new HashMap>(); + // from String to Vector of XPropertyChangeListener + private HashMap> vetoListeners + = new HashMap>(); + // 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 0000000000..22ea695f5b --- /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 + * + * + * [uno:]connection-type,parameters;protocol-name,parameters;objectname"; + * + * + * An example Uno Url will look like this: + * + * + * socket,host=localhost,port=2002;urp;StarOffice.ServiceManager + * + * + * For more information about Uno Url please consult + * + * http://udk.openoffice.org/common/man/spec/uno-url.html + * + * Usage: + * + * + * UnoUrl url = UnoUrl.parseUnoUrl("uno:socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"); + * + */ +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 partParameters; + private final String uninterpretedParameterString; + + public UnoUrlPart( + String uninterpretedParameterString, + String partTypeName, + HashMap partParameters) { + this.uninterpretedParameterString = uninterpretedParameterString; + this.partTypeName = partTypeName; + this.partParameters = partParameters; + } + + public String getPartTypeName() { + return partTypeName; + } + + public HashMap 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 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 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 buildParamHashMap(String paramString) + throws com.sun.star.lang.IllegalArgumentException { + HashMap params = new HashMap(); + + 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 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 0000000000..67e01ac327 --- /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 m_weakRef; + // contains XReference objects registered by addReference + private final List m_xreferenceList; + + /** + *@param component the object that is to be held weak + */ + public WeakAdapter(Object component) + { + m_weakRef= new WeakReference(component); + m_xreferenceList= Collections.synchronizedList( new LinkedList()); + } + + /** 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 0000000000..ac175d3a6d --- /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,Type[]> _mapTypes = new HashMap,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 vec= new ArrayList(); + 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 0000000000..26c2d449d6 --- /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. + * + *

This class is not synchronized, as any necessary synchronization will already + * take place in the client.

+ */ +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 map = new HashMap(); // 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 0000000000..106a8736cb --- /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 0000000000..928cdcd631 --- /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 s = map.get(tid); + if (s == null) { + s = new Stack(); + map.put(tid, s); + } + s.push(item); + } + + public synchronized Item pop(ThreadId tid) { + Stack 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> map = new HashMap>(); // 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 0000000000..af37838fbc --- /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 Float.valueOf(input.readFloat()); + } + + private Double readDoubleValue() throws IOException { + return Double.valueOf(input.readDouble()); + } + + private Character readCharValue() throws IOException { + return Character.valueOf(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 0000000000..34cdf70739 --- /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 0000000000..0ce9d35be7 --- /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 com.sun.star.bridge.XProtocolProperties, + * 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 releaseQueue = new ArrayList(); // 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 0000000000..6dcdc99db9 --- /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. + *

+ * @return the name + */ + public TypeDescription getTypeDescription() { + return typeDescription; + } + + /** + * Gives native java field of this member. + *

+ * @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 0000000000..27a5361f32 --- /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 0000000000..6cbbb1678f --- /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 oneWay, + * 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 TypeDescription of + * the [in] parameters. + * @return the in parameters + */ + public TypeDescription[] getInSignature() { + return inSignature; + } + + /** + * Gives any array of TypeDescription of + * the [out] parameters. + * @return the out parameters + */ + public TypeDescription[] getOutSignature() { + return outSignature; + } + + /** + * Gives the TypeDescription of + * the return type. + * @return the return type TypeDescription + */ + 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 0000000000..a3c3bcd528 --- /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 TypeDescription of the + * super, if it exists. + * @return the TypeDescription. + */ + public TypeDescription getSuperType() { + // Arbitrarily take the first super type: + return superTypes == null || superTypes.length == 0 + ? null : superTypes[0]; + } + + /** + * Gets the MethodDescription for every + * method, if this type is an interface. Otherwise + * returns null. + * @return the MethodDescription[]. + */ + public MethodDescription[] getMethodDescriptions() { + initMethodDescriptions(); + return methodDescriptions; //TODO: clone? + } + + /** + * Gets the MethodDescription for the + * method with index methodId, if it exists, otherwise + * returns null. + * + * @param methodId the index. + * + * @return the MethodDescription. + */ + 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 MethodDescription for the + * method with the name name, if it exists, + * otherwise returns null. + * + * @param name the name of the method. + * + * @return the MethodDescription. + */ + 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 FieldDescription for every + * field, if this type is an interface. Otherwise + * returns null. + * @return the FieldDescription[]. + */ + public FieldDescription[] getFieldDescriptions() { + return fieldDescriptions; //TODO: clone? + } + + /** + * Gets the FieldDescription for the + * field with the name name, if it exists, + * otherwise returns null. + * + * @param name the name of the field. + * + * @return the FieldDescription. + */ + 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 TypeClass of the type. + * @return the TypeClass. + */ + public TypeClass getTypeClass() { + return typeClass; + } + + /** + * Gets the component TypeDescription if + * this is an array type, otherwise returns null. + * @return the TypeDescription + */ + public TypeDescription getComponentType() { + return componentType; + } + + /** + * Gets the (UNO) type name. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Mapping from UNO types to type names
UNO typetype name
VOID"void"
BOOLEAN"boolean"
CHAR"char"
BYTE"byte"
SHORT"short"
UNSIGNED SHORT"unsigned short"
LONG"long"
UNSIGNED LONG"unsigned long"
HYPER"hyper"
UNSIGNED HYPER"unsigned hyper"
FLOAT"float"
DOUBLE"double"
STRING"string"
TYPE"type"
ANY"any"
sequence type of base type T"[]" followed by type name for T
enum type named NN (see below)
struct type named NN (see below)
exception type named NN (see below)
interface type named NN (see below)
+ *

For a UNO type named N, consisting of a sequence of module + * names M1, ..., Mn followed by + * a simple name S, the corresponding type name consists of the + * same sequence of module names and simple name, with "." + * separating the individual elements.

+ * @return the type name. + */ + public String getTypeName() { + return typeName; + } + + /** + * Gets the (Java) array type name. + *

The array type name is defined to be the Java class name (as returned + * by Class.forName) of the Java array class that corresponds + * to the UNO sequence type with this type (the UNO type represented by this + * TypeDescription instance) as base type. For an + * TypeDescription instance representing the UNO type VOID, + * the array type name is defined to be + * "[Ljava.lang.Void;".

+ * @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 superList = new ArrayList(); + 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 directList = new ArrayList(); + 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 args = new java.util.ArrayList(); + 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 list = new ArrayList(); + } + + 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 { + public Entry(TypeDescription desc, ReferenceQueue queue) { + super(desc, queue); + typeName = desc.getTypeName(); + } + + public final String typeName; + } + + private final HashMap map = new HashMap(); + private final ReferenceQueue queue = new ReferenceQueue(); + } + + 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 0000000000..3230528e14 --- /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 1.2 type. + + @param name the name of this attribute; must not be null + + @param index the index among the direct members + + @param flags any flags (READONLY, BOUND, + UNSIGNED, ANY, INTERFACE) + + @param unoType the exact UNO type; or null if the UNO type + is already unambiguously represented by the Java 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 1.2 type. + + @return the exact UNO type of this attribute type info, or + null if the UNO type is already unambiguously represented by + the Java 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 0000000000..2676718cf9 --- /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 UNOTYPEINFO 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 0000000000..5b3f669fee --- /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 1.2 type. + + @param name the name of this member; must not be null + + @param index the index among the direct members + + @param flags any flags (UNSIGNED, ANY, + INTERFACE, TYPE_PARAMETER) + + @param unoType the exact UNO type; or null if the UNO type + is already unambiguously represented by the Java 1.2 type + + @param typeParameterIndex the index of the type parameter that determines + the type of this parameterized member; or -1 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 1.2 type. + + @return the exact UNO type of this member type info, or null + if the UNO type is already unambiguously represented by the Java 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, -1 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 0000000000..319e63514c --- /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 1.2 type. + + @param name the name of this method; must not be null + + @param index the index among the direct members + + @param flags any flags (ONEWAY, UNSIGNED, + ANY, INTERFACE) + + @param unoType the exact UNO return type; or null if the UNO + type is already unambiguously represented by the Java 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 1.2 type. + + @return the exact UNO return type of this method type info, or + null if the UNO type is already unambiguously represented by + the Java 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 0000000000..f15ddc816a --- /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 1.2 type. + + @param name the name of this parameter; must not be null + + @param methodName the name of the method; must not be null + + @param index the index among the parameters + + @param flags any flags (IN, OUT, + UNSIGNED, ANY, INTERFACE) + + @param unoType the exact UNO type; or null if the UNO type + is already unambiguously represented by the Java 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 1.2 type. + +

If this is an out or in–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.

+ + @return the exact UNO type of this parameter type info, or + null if the UNO type is already unambiguously represented by + the Java 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 0000000000..2503029fe0 --- /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. + +

Only used in the flags argument of the + AttributeTypeInfo constructors.

+ + @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 0000000000..588b8fe388 --- /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. + * + *

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).

+ * + *

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().

+ */ +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. + * + *

The run method of the given job is called exactly once. If it terminates + * abnormally by throwing any Throwable, that is ignored.

+ * + * @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 Runnable, except that the run method may throw any + * Throwable (which is effectively ignored by + * AsynchronousFinalizer.add, similar to any Throwables + * raised by finalize being ignored). + */ + public interface Job { + void run() throws Throwable; + } + + private final LinkedList queue = new LinkedList(); + 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 0000000000..6fdde2a8d7 --- /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 DisposeNotifier. + * + * @see DisposeNotifier + */ +public interface DisposeListener { + /** + * Callback fired by a DisposeNotifier once it is disposed. + * + * @param source the DisposeNotifier that fires the callback; + * will never be null + */ + 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 0000000000..ebe0008d07 --- /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 + * WeakMap. + * + * @see WeakMap + */ +public interface DisposeNotifier { + /** + * Adds a dispose listener, to be notified when this object is disposed. + * + *

It is unspecified what happens when the same listener is added + * multiple times.

+ * + *

It is unspecified exactly when the notifyDispose 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 notifyDispose callback.

+ * + * @param listener a dispose listener, to be notified when this object is + * disposed; must not be null + */ + 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 0000000000..eb5c6af34e --- /dev/null +++ b/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java @@ -0,0 +1,132 @@ +/* -*- 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; + +/** + * Helper functions to locate and load native files. + * + *

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.

+ */ +public final class NativeLibraryLoader { + /** + * Load a system library, using a given class loader to locate the library. + * + *

This is similar to System.loadLibrary.

+ * + * @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. + * + *

This is similar to ClassLoader.getResource, but only works + * for local resources (local files), and adds additional functionality for + * URLClassLoaders.

+ * + * @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 0000000000..de8b5253aa --- /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 0000000000..131890f54f --- /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 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 0000000000..4d63d27286 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/WeakMap.java @@ -0,0 +1,323 @@ +/* + * 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 WeakReference. + * + *

Like HashMap, this implementation provides all of the + * optional map operations, and permits the null key.

+ * + *

Also like HashMap, this implementation is not synchronized. + * If multiple threads share an instance, and at least one of them executes any + * modifying operations on the WeakMap, they have to use external + * synchronization.

+ * + *

Unlike other map implementations, WeakMap is asymmetric in + * that put expects the given value to be a plain object that is + * then wrapped in a WeakReference, while the occurrences of values + * in all other methods (containsValue, entrySet, + * equals, get, hashCode, + * remove, values, and also the return value of + * put) expect already wrapped instances of + * WeakReference. That is, after weakMap.put("key", + * o), weakMap.get("key").equals(o) does not work as + * naïvely expected; neither does + * weakMap1.putAll(weakMap2).

+ * + *

At an arbitrary time after the WeakReference value of an + * entry has been cleared by the garbage collector, the entry is automatically + * removed from the map.

+ * + *

Values placed into a WeakMap may optionally support the + * DisposeNotifier interface. For those that do, the associated + * WeakReference wrappers are automatically cleared as soon as the + * values are disposed.

+ * + * 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 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> map = new HashMap>(); + private final ReferenceQueue queue = new ReferenceQueue(); + + /** + * Constructs an empty WeakMap. + */ + public WeakMap() {} + + /** + * Constructs a new WeakMap with the same mappings as the + * specified Map. + * + * @param m the map whose mappings are to be placed in this map + */ + public WeakMap(Map m) { + putAll(m); + } + + /** + * Returns the number of key–value mappings in this map. + * + *

This is a non-modifying operation.

+ * + * @return the number of key–value mappings in this map + */ + public int size() { + return map.size(); + } + + /** + * Returns true if this map contains no key–value + * mappings. + * + *

This is a non-modifying operation.

+ * + * @return true if this map contains no key–value + * mappings + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Returns true if this map contains a mapping for the + * specified key. + * + *

This is a non-modifying operation.

+ * + * @param key the key whose presence in this map is to be tested + * @return true if this map contains a mapping for the + * specified key + */ + public boolean containsKey(/*K*/ Object key) { + return map.containsKey(key); + } + + /** + * Returns true if this map maps one or more keys to the + * specified value. + * + *

This is a non-modifying operation.

+ * + * @param value the value whose presence in this map is to be tested + * @return true if this map maps one or more keys to the + * specified value + */ + public boolean containsValue(Object /*WeakReference*/ value) { + return map.containsValue(value); + } + + /** + * Returns the value to which the specified key is mapped in this map, or + * null if the map contains no mapping for this key. + * + *

This is a non-modifying operation.

+ * + * @param key the key whose associated value is to be returned + * + * @return the value to which this map maps the specified key, or + * null if the map contains no mapping for this key + */ + public WeakReference get(/*K*/ Object key) { + return map.get(key); + } + + /** + * Associates the specified value with the specified key in this map. + * + *

This is a modifying operation.

+ * + * @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 + * WeakReference. + * @return previous value associated with the specified key, or + * null if there was no mapping for the key + */ + @SuppressWarnings("unchecked") + public Object /*WeakReference*/ put(/*K*/ Object key, /*V*/ Object value) { + cleanUp(); + return map.put((K) key, new Entry((K) key, (V) value, queue)); + } + + /** + * Removes the mapping for this key from this map if present. + * + *

This is a modifying operation.

+ * + * @param key the key whose mapping is to be removed from the map + * @return previous value associated with the specified key, or + * null if there was no mapping for the key + */ + public Object /*WeakReference*/ remove(/*K*/ Object key) { + cleanUp(); + return map.remove(key); + } + + /** + * Copies all of the mappings from the specified map to this map. + * + *

This is a modifying operation.

+ * + * @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 + * WeakReference. + */ + @SuppressWarnings("unchecked") + public void putAll(Map/**/ m) { + cleanUp(); + for (Iterator> i = m.entrySet().iterator(); i.hasNext();) { + Map.Entry e = i.next(); + K k = e.getKey(); + map.put(k, new Entry(k, e.getValue(), queue)); + } + } + + /** + * Removes all mappings from this map. + * + *

This is a modifying operation.

+ */ + public void clear() { + cleanUp(); + map.clear(); + } + + /** + * Returns a view of the keys contained in this map. + * + *

This is a non-modifying operation.

+ * + * @return a set view of the keys contained in this map + */ + public Set keySet() { + return map.keySet(); + } + + /** + * Returns a collection view of the values contained in this map. + * + *

This is a non-modifying operation.

+ * + * @return a collection view of the values contained in this map + */ + public Collection> values() { + return map.values(); + } + + /** + * Returns a collection view of the mappings contained in this map. + * + *

This is a non-modifying operation.

+ * + * @return a collection view of the mappings contained in this map + */ + public Set/*>>*/ 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 WeakReference, silently handling a + * null argument. + * + *

This static method is useful to wrap around the return values of + * methods like get.

+ * + * @param ref must be either an instance of WeakReference or + * null + * @return the referent of the specified WeakReference, or + * null if ref is null + */ + @SuppressWarnings("unchecked") + public static T getValue(Object /*WeakReference*/ ref) { + return ref == null ? null : ((WeakReference) 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. + **/ + @SuppressWarnings("unchecked") + private void cleanUp() { + for (;;) { + Entry e = (Entry) 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 extends WeakReference + implements DisposeListener + { + private final K key; + + private Entry(K key, V value, ReferenceQueue 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(); + } + } + +} diff --git a/ridljar/com/sun/star/uno/Any.java b/ridljar/com/sun/star/uno/Any.java new file mode 100644 index 0000000000..2f4976436d --- /dev/null +++ b/ridljar/com/sun/star/uno/Any.java @@ -0,0 +1,151 @@ +/* + * 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.uno; + + +/** + * The UNO IDL type any is mapped to java type java.lang.Object. + *

+ * In special cases it is necessary to have an explicit any to additionally transport + * an exact type. For instance if you want to pass an object reference via + * an interprocess connection using an any, you should use this class to add + * an explicit interface type, so the remote counterpart doesn't need to invoke + * a queryInterface). + *

+ */ +public class Any { + /** + * The type of the any. + * + * @see #getType + */ + protected Type _type; + + /** + * The data of the any. + * + * @see #getObject + */ + protected Object _object; + + public static final Any VOID = new Any(new Type("void", TypeClass.VOID), + null); + // do not use Type.VOID here to avoid circular dependencies between + // static members of Any and Type + + /** + * Constructs a new any. + * + * @param zInterface the type of the any. + * @param object the data of the any. + * @deprecated as of UDK 2.0 + */ + @Deprecated + public Any(Class zInterface, Object object) { + this(new Type(zInterface), object); + } + + /** + * Constructs a new any with a given type and value + * + * @param type the UNO type of the any. + * @param object the value of the any. + */ + public Any(Type type, Object object) { + if (type.equals(Type.ANY)) { + throw new IllegalArgumentException("Any cannot contain Any"); + } + _type = type; + _object = object; + } + + /** + * Complete a UNO ANY (make sure it is wrapped up as an + * Any instance). + * + * @param any a Java value representing a UNO ANY value. + * @return a complete Java value (that is, an Any instance) + * representing the same UNO ANY value as the given argument. + * @since UDK 3.2.3 + */ + public static final Any complete(Object any) { + return any instanceof Any + ? (Any) any + : new Any( + new Type(any == null ? XInterface.class : any.getClass()), any); + } + + /** + * Gets the type of the value within the any. + * + * @return the type of the value within the any. + */ + public Type getType() { + return _type; + } + + /** + * Gets the value within the any. + * + * @return gets the value within the any. + */ + public Object getObject() { + return _object; + } + + /** + * Indicates whether some other object is equal to this one. + * + * @param obj the reference object with which to compare. + * @return true if this object is the same as the obj argument; + * false otherwise. + * @see java.lang.Object#equals + */ + @Override + public boolean equals(Object obj) { + return obj instanceof Any && _type.equals(((Any) obj)._type) + && (_object == null + ? ((Any) obj)._object == null + : _object.equals(((Any) obj)._object)); + } + + /** + * 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() { + return _type.hashCode() * 13 + + (_object == null ? 0 : _object.hashCode()); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return "Any[" + _type + ", " + _object + "]"; + } +} diff --git a/ridljar/com/sun/star/uno/AnyConverter.java b/ridljar/com/sun/star/uno/AnyConverter.java new file mode 100644 index 0000000000..b37eab254e --- /dev/null +++ b/ridljar/com/sun/star/uno/AnyConverter.java @@ -0,0 +1,668 @@ +/* -*- 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.uno; + +/** + * This class provides static methods which aim at exploring the contents of an + * Any and extracting its value. + * + *

All public methods take an Object argument that either is the immediate object, + * such as Boolean, Type, interface implementation, or an Any that contains an + * object.

+ * + *

The methods which extract the value do a widening conversion. See the + * method comments for the respective conversions.

+ */ +public class AnyConverter +{ + /** + * Determines the type of an Any object. + * + * @param object any object. + * @return type object. + */ + public static Type getType( Object object ) + { + Type t; + if (null == object) + { + t = m_XInterface_type; + } + else if (object instanceof Any) + { + t = ((Any)object).getType(); + // nested any + if (TypeClass.ANY_value == t.getTypeClass().getValue()) + return getType( ((Any)object).getObject() ); + } + else + { + t = new Type( object.getClass() ); + } + return t; + } + + /** + * Checks if the any contains the idl type void. + * + * @param object the object to check. + * @return true when the any is void, false otherwise. + */ + public static boolean isVoid(Object object){ + return containsType(TypeClass.VOID, object); + } + + /** + * Checks if the any contains a value of the idl type char. + * + * @param object the object to check. + * @return true when the any contains a char, false otherwise. + */ + public static boolean isChar(Object object){ + return containsType(TypeClass.CHAR, object); + } + + /** + * Checks if the any contains a value of the idl type boolean. + * + * @param object the object to check. + * @return true when the any contains a boolean, false otherwise. + */ + public static boolean isBoolean(Object object){ + return containsType(TypeClass.BOOLEAN, object); + } + + /** + * Checks if the any contains a value of the idl type byte. + * + * @param object the object to check. + * @return true when the any contains a byte, false otherwise. + */ + public static boolean isByte(Object object){ + return containsType(TypeClass.BYTE, object); + } + + /** + * Checks if the any contains a value of the idl type short. + * + * @param object the object to check. + * @return true when the any contains a short, false otherwise. + */ + public static boolean isShort(Object object){ + return containsType(TypeClass.SHORT, object); + } + + /** + * Checks if the any contains a value of the idl type long + * (which maps to a java-int). + * + * @param object the object to check. + * @return true when the any contains a int, false otherwise. + */ + public static boolean isInt(Object object){ + return containsType(TypeClass.LONG, object); + } + + /** + * Checks if the any contains a value of the idl type hyper + * (which maps to a java-long). + * + * @param object the object to check. + * @return true when the any contains a long, false otherwise. + */ + public static boolean isLong(Object object){ + return containsType(TypeClass.HYPER, object); + } + + /** + * Checks if the any contains a value of the idl type float. + * + * @param object the object to check. + * @return true when the any contains a float, false otherwise. + */ + public static boolean isFloat(Object object){ + return containsType(TypeClass.FLOAT, object); + } + + /** + * Checks if the any contains a value of the idl type double. + * + * @param object the object to check. + * @return true when the any contains a double, false otherwise. + */ + public static boolean isDouble(Object object){ + return containsType(TypeClass.DOUBLE, object); + } + + /** + * Checks if the any contains a value of the idl type string. + * + * @param object the object to check. + * @return true when the any contains a string, false otherwise. + */ + public static boolean isString(Object object){ + return containsType(TypeClass.STRING, object); + } + + /** + * Checks if the any contains a value of the idl type enum. + * + * @param object the object to check. + * @return true if the any contains an enum, false otherwise. + */ + public static boolean isEnum(Object object) + { + return containsType(TypeClass.ENUM, object); + } + + /** + * Checks if the any contains a value of the idl type type. + * + * @param object the object to check. + * @return true when the any contains a type, false otherwise. + */ + public static boolean isType(Object object){ + return containsType(TypeClass.TYPE, object); + } + + /** + * Checks if the any contains an interface, struct, exception, sequence or enum. + * + *

If object is an any with an interface type, then true is also + * returned if the any contains a null reference. This is because interfaces + * are allowed to have a null value contrary to other UNO types.

+ * + * @param object the object to check. + * @return true if the any contains an object. + */ + public static boolean isObject(Object object) + { + int tc = getType(object).getTypeClass().getValue(); + return (TypeClass.INTERFACE_value == tc || + TypeClass.STRUCT_value == tc || + TypeClass.EXCEPTION_value == tc || + TypeClass.SEQUENCE_value == tc || + TypeClass.ENUM_value == tc); + } + + /** + * Checks if the any contains UNO idl sequence value (meaning a java array + * containing elements which are values of UNO idl types). + * + * @param object the object to check. + * @return true when the any contains an object which implements interfaces, + * false otherwise. + */ + public static boolean isArray(Object object){ + return containsType(TypeClass.SEQUENCE, object); + } + + /** + * Converts a Char object or an Any object containing a Char object into a + * simple char. + * + * @param object the object to convert. + * @return the char contained within the object. + * @throws com.sun.star.lang.IllegalArgumentException in case no char is + * contained within object. + * + * @see #isChar + */ + public static char toChar(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Character ret= (Character)convertSimple(TypeClass.CHAR, null, object); + return ret.charValue(); + } + + /** + * Converts a Boolean object or an Any object containing a Boolean object + * into a simple boolean. + * + * @param object the object to convert. + * @return the boolean contained within the object + * @throws com.sun.star.lang.IllegalArgumentException in case no boolean is + * contained within object + * + * @see #isBoolean + */ + public static boolean toBoolean(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Boolean ret= (Boolean)convertSimple(TypeClass.BOOLEAN, null, object); + return ret.booleanValue(); + } + + /** + * Converts a Byte object or an Any object containing a Byte object into a + * simple byte. + * + * @param object the object to convert. + * @return the boolean contained within the object. + * @throws com.sun.star.lang.IllegalArgumentException in case no byte is + * contained within object. + * + * @see #isBoolean + */ + public static byte toByte(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Byte ret= (Byte)convertSimple(TypeClass.BYTE, null, object); + return ret.byteValue(); + } + + /** + * Converts a number object into a simple short and allows widening conversions. + * + *

Allowed argument types are Byte, Short or Any containing these types.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no short or + * byte is contained within object. + * + * @return the short contained within the object. + */ + public static short toShort(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Short ret= (Short)convertSimple(TypeClass.SHORT, null, object); + return ret.shortValue(); + } + /** + * Converts a number object into an idl unsigned short and allows widening + * conversions. + * + *

Allowed argument types are Anies containing idl unsigned short values.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no idl unsigned + * short is contained within Any. + * + * @return an (unsigned) short. + */ + public static short toUnsignedShort(Object object) + throws com.sun.star.lang.IllegalArgumentException + { + Short ret= (Short)convertSimple(TypeClass.UNSIGNED_SHORT, null, object); + return ret.shortValue(); + } + + /** + * Converts a number object into a simple int and allows widening conversions. + * + *

Allowed argument types are Byte, Short, Integer or Any containing these + * types.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no short, byte + * or int is contained within object. + * + * @return the int contained within the object. + */ + public static int toInt(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Integer ret= (Integer) convertSimple( TypeClass.LONG, null, object); + return ret.intValue(); + } + /** + * Converts a number object into an idl unsigned long and allows widening + * conversions. + * + *

Allowed argument types are Anies containing idl unsigned short or + * unsigned long values.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no idl unsigned + * short nor unsigned long is contained within Any. + * + * @return an (unsigned) int. + */ + public static int toUnsignedInt(Object object) + throws com.sun.star.lang.IllegalArgumentException + { + Integer ret = (Integer)convertSimple(TypeClass.UNSIGNED_LONG, null, object); + return ret.intValue(); + } + + /** + * Converts a number object into a simple long and allows widening conversions. + * + *

Allowed argument types are Byte, Short, Integer, Long or Any containing + * these types.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no short, byte, + * int or long is contained within object. + * + * @return the long contained within the object. + */ + public static long toLong(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Long ret= (Long) convertSimple( TypeClass.HYPER, null, object); + return ret.longValue(); + } + /** + * Converts a number object into an idl unsigned hyper and allows widening + * conversions. + * + *

Allowed argument types are Anies containing idl unsigned short, unsigned + * long or unsigned hyper values.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no idl unsigned + * short, nor unsigned long nor unsigned hyper is contained within object. + * + * @return an (unsigned) long. + */ + public static long toUnsignedLong(Object object) + throws com.sun.star.lang.IllegalArgumentException + { + Long ret = (Long)convertSimple(TypeClass.UNSIGNED_HYPER, null, object); + return ret.longValue(); + } + + /** + * Converts a number object into a simple float and allows widening conversions. + * + *

Allowed argument types are Byte, Short, Float or Any containing these + * types.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no byte, short + * or float is contained within object. + * + * @return the float contained within the object. + */ + public static float toFloat(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Float ret= (Float) convertSimple( TypeClass.FLOAT,null, object); + return ret.floatValue(); + } + + /** + * Converts a number object into a simple double and allows widening conversions. + * + *

Allowed argument types are Byte, Short, Int, Float, Double or Any + * containing these types.

+ * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no byte, short, + * int, float or double is contained within object. + * + * @return the double contained within the object. + */ + public static double toDouble(Object object) throws com.sun.star.lang.IllegalArgumentException { + Double ret= (Double) convertSimple( TypeClass.DOUBLE, null, object); + return ret.doubleValue(); + } + + /** + * Converts a string or an any containing a string into a string. + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no string is + * contained within object. + * + * @return the string contained within the object. + */ + public static String toString(Object object) throws com.sun.star.lang.IllegalArgumentException { + return (String) convertSimple( TypeClass.STRING, null, object); + } + + /** + * Converts a Type or an any containing a Type into a Type. + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no type is + * contained within object. + * + * @return the type contained within the object. + */ + public static Type toType(Object object) throws com.sun.star.lang.IllegalArgumentException { + return (Type) convertSimple( TypeClass.TYPE, null, object); + } + + /** + * Converts a UNO object (struct, exception, sequence, enum or interface) or + * an Any containing these types into a UNO object of a specified destination + * type. + * + *

For interfaces, the argument object is queried for the interface + * specified by the type argument.

+ * + *

That query (UnoRuntime.queryInterface) might return null, if the interface + * is not implemented or a null-ref or a VOID any is given.

+ * + * @param type type of the returned value. + * @param object the object that is to be converted. + * @throws com.sun.star.lang.IllegalArgumentException in case conversion is + * not possible. + * + * @return destination object. + */ + public static Object toObject(Type type, Object object) + throws com.sun.star.lang.IllegalArgumentException + { + return convertSimple( type.getTypeClass(), type, object ); + } + /** + * Converts a UNO object (struct, exception, sequence, enum or interface) or + * an Any containing these types into a UNO object of a specified destination + * type. + * + *

For interfaces, the argument object is queried for the interface + * specified by the type argument. That query (UnoRuntime.queryInterface) + * might return null, if the interface is not implemented or a null-ref or a + * VOID any is given.

+ * + * @param clazz class of the returned value. + * @param object the object that is to be converted. + * @throws com.sun.star.lang.IllegalArgumentException in case conversion is + * not possible. + * + * @return destination object. + */ + public static Object toObject(Class clazz, Object object) + throws com.sun.star.lang.IllegalArgumentException + { + return toObject( new Type( clazz ), object ); + } + + /** + * Converts an array or an any containing an array into an array. + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no array is + * contained within object. + * + * @return the array contained within the object. + */ + public static Object toArray( Object object) throws com.sun.star.lang.IllegalArgumentException { + return convertSimple( TypeClass.SEQUENCE, null, object); + } + + /** + * Examines the argument object if is correspond to the type in + * argument what. + * + *

object is either matched directly against the type or if it is + * an any then the contained object is matched against the type.

+ */ + private static boolean containsType( TypeClass what, Object object){ + return (getType(object).getTypeClass().getValue() == what.getValue()); + } + + private static final Type m_XInterface_type = new Type( XInterface.class ); + + private static Object convertSimple( TypeClass destTClass, Type destType, Object object_ ) + throws com.sun.star.lang.IllegalArgumentException + { + Object object; + Type type; + if (object_ instanceof Any) { + // unbox + Any a = (Any)object_; + object = a.getObject(); + type = a.getType(); + // nested any + if (TypeClass.ANY_value == type.getTypeClass().getValue()) + return convertSimple( destTClass, destType, object ); + } else { + object = object_; + type = (null == object ? m_XInterface_type : new Type( object.getClass() )); + } + + int tc = type.getTypeClass().getValue(); + int dest_tc = destTClass.getValue(); + + if (null == object) { + // special for interfaces + if (TypeClass.INTERFACE_value == tc && dest_tc == tc) + return null; + } else { + switch (dest_tc) { + case TypeClass.CHAR_value: + if (tc == TypeClass.CHAR_value) + return object; + break; + case TypeClass.BOOLEAN_value: + if (tc == TypeClass.BOOLEAN_value) + return object; + break; + case TypeClass.BYTE_value: + if (tc == TypeClass.BYTE_value) + return object; + break; + case TypeClass.SHORT_value: + switch (tc) { + case TypeClass.BYTE_value: + return Short.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + return object; + } + break; + case TypeClass.UNSIGNED_SHORT_value: + switch (tc) { + case TypeClass.UNSIGNED_SHORT_value: + return object; + } + break; + case TypeClass.LONG_value: + switch (tc) { + case TypeClass.BYTE_value: + return Integer.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return Integer.valueOf( ((Short)object).shortValue() ); + case TypeClass.LONG_value: + return object; + } + break; + case TypeClass.UNSIGNED_LONG_value: + switch (tc) { + case TypeClass.UNSIGNED_SHORT_value: + return Integer.valueOf( ((Short)object).shortValue() ); + case TypeClass.UNSIGNED_LONG_value: + return object; + } + break; + case TypeClass.HYPER_value: + switch (tc) { + case TypeClass.BYTE_value: + return Long.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return Long.valueOf( ((Short)object).shortValue() ); + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + return Long.valueOf( ((Integer)object).intValue() ); + case TypeClass.HYPER_value: + return object; + } + break; + case TypeClass.UNSIGNED_HYPER_value: + switch (tc) { + case TypeClass.UNSIGNED_SHORT_value: + return Long.valueOf( ((Short)object).shortValue() ); + case TypeClass.UNSIGNED_LONG_value: + return Long.valueOf( ((Integer)object).intValue() ); + case TypeClass.UNSIGNED_HYPER_value: + return object; + } + break; + case TypeClass.FLOAT_value: + switch (tc) { + case TypeClass.BYTE_value: + return Float.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + return Float.valueOf( ((Short)object).shortValue() ); + case TypeClass.FLOAT_value: + return object; + } + break; + case TypeClass.DOUBLE_value: + switch (tc) { + case TypeClass.BYTE_value: + return Double.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + return Double.valueOf( ((Short)object).shortValue() ); + case TypeClass.LONG_value: + return Double.valueOf( ((Integer)object).intValue() ); + case TypeClass.FLOAT_value: + return Double.valueOf( ((Float)object).floatValue() ); + case TypeClass.DOUBLE_value: + return object; + } + break; + case TypeClass.ENUM_value: + if (tc == TypeClass.ENUM_value && + (null == destType || destType.equals( type ) /* optional destType */)) + { + return object; + } + break; + case TypeClass.STRING_value: + if (tc == TypeClass.STRING_value) + return object; + break; + case TypeClass.TYPE_value: + if (tc == TypeClass.TYPE_value) + return object; + break; + case TypeClass.INTERFACE_value: + // Because object is a class, not an interface, it is + // controversial what kind of Type "new Type(object.class)" + // above should return (UNKNOWN or INTERFACE), so that we should + // not check here for "tc == TypeClass.INTERFACE_value". + // Instead, we check whether object (indirectly) derives from + // XInterface: + if (object instanceof XInterface) + return UnoRuntime.queryInterface( destType, object ); + break; + case TypeClass.STRUCT_value: + case TypeClass.EXCEPTION_value: + if (destType.isSupertypeOf(type)) { + return object; + } + break; + case TypeClass.SEQUENCE_value: + if (tc == TypeClass.SEQUENCE_value && + (null == destType || destType.equals( type ) /* optional destType */)) + { + return object; + } + break; + } + } + throw new com.sun.star.lang.IllegalArgumentException( + "The Argument did not hold the proper type"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/Ascii.java b/ridljar/com/sun/star/uno/Ascii.java new file mode 100644 index 0000000000..ce51088935 --- /dev/null +++ b/ridljar/com/sun/star/uno/Ascii.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.uno; + +/** + * The Ascii class represents the IDL build in type ascii. + * + * @deprecated do not use. + */ +@Deprecated +public final class Ascii { + public final char ascii; + + /** + * Constructs a new Ascii. + * + * @deprecated do not use. + * @param c the char value. + */ + @Deprecated + public Ascii(char c) { + ascii = c; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/AsciiString.java b/ridljar/com/sun/star/uno/AsciiString.java new file mode 100644 index 0000000000..8b744c5a22 --- /dev/null +++ b/ridljar/com/sun/star/uno/AsciiString.java @@ -0,0 +1,44 @@ +/* -*- 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.uno; + +/** + * The Ascii class represents the IDL build in type asciistring. + * + * @deprecated do not use. + */ +@Deprecated +public final class AsciiString { + public final String asciistring; + + /** + * Constructs a new AsciiString. + * + * @deprecated do not use. + * @param s the String value. + */ + @Deprecated + public AsciiString(String s) { + asciistring = s; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/Enum.java b/ridljar/com/sun/star/uno/Enum.java new file mode 100644 index 0000000000..8bd50876dd --- /dev/null +++ b/ridljar/com/sun/star/uno/Enum.java @@ -0,0 +1,50 @@ +/* + * 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.uno; + +/** + * The Enum class is the base class for all classes generated + * as java binding for the IDL type enum. + *

+ * Each java mapped enum class provides static member of this class + * which represents the enum values. + * You cannot create an object of this class or subclass direct, to + * avoid enum values with integer values outside the defined range. + *

+ */ +public abstract class Enum { + private final int m_value; + + /** + * Constructs an enum value. + * @param value the integer value of this enum value. + */ + protected Enum(int value) { + m_value = value; + } + + /** + * Get the integer value of an enum value. + * @return the integer value. + */ + public final int getValue() { + return m_value; + } +} + diff --git a/ridljar/com/sun/star/uno/IBridge.java b/ridljar/com/sun/star/uno/IBridge.java new file mode 100644 index 0000000000..5cd9fda704 --- /dev/null +++ b/ridljar/com/sun/star/uno/IBridge.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.uno; + +import java.io.IOException; + +/** + * This is abstract interface for bridges. + * + *

Bridges are able to map one object from one UNO environment to another and + * vice versa.

+ * + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IQueryInterface + * @see com.sun.star.uno.UnoRuntime + * + * @deprecated As of UDK 3.2, this interface is deprecated, without offering a + * replacement. + */ +@Deprecated +public interface IBridge { + /** + * Maps an object from the source environment to the destination + * environment. + * + * @param object the object to map + * @param type the type of the interface that shall be mapped + * @return the object in the destination environment + */ + Object mapInterfaceTo(Object object, Type type); + + /** + * Maps an object from the destination environment to the source + * environment. + * + * @param object the object to map + * @param type the type of the interface that shall be mapped + * @return the object in the source environment + */ + Object mapInterfaceFrom(Object object, Type type); + + /** + * Returns the source environment. + * + * @return the source environment of this bridge + */ + IEnvironment getSourceEnvironment(); + + /** + * Returns the destination environment. + * + * @return the destination environment of this bridge + */ + IEnvironment getTargetEnvironment(); + + /** + * Increases the life count. + */ + void acquire(); + + /** + * Decreases the life count. + * + *

If the life count drops to zero, the bridge disposes itself.

+ */ + void release(); + + /** + * Disposes the bridge. + * + *

Sends involved threads an InterruptedException. Releases + * mapped objects.

+ * + * @throws InterruptedException it's deprecated so who cares. + * @throws IOException it's deprecated so who cares. + */ + void dispose() throws InterruptedException, IOException; +} diff --git a/ridljar/com/sun/star/uno/IEnvironment.java b/ridljar/com/sun/star/uno/IEnvironment.java new file mode 100644 index 0000000000..5d32893052 --- /dev/null +++ b/ridljar/com/sun/star/uno/IEnvironment.java @@ -0,0 +1,144 @@ +/* + * 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.uno; + +/** + * The interface implemented by UNO environments. + * + *

With this interface, objects can be registered at and revoked from an + * environment.

+ * + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IQueryInterface + * @see com.sun.star.uno.UnoRuntime + * + * @deprecated As of UDK 3.2, this interface is deprecated, without offering a + * replacement. + */ +@Deprecated +public interface IEnvironment { + /** + * Gets the context of this environment. + * + * @return the context of this environment + */ + Object getContext(); + + /** + * Gets the name of this environment. + * + * @return the name of this environment + */ + String getName(); + + /** + * Registers one UNO interface facet of an object. + * + *

Such an object will typically be one of three things: + *

    + *
  • A local Java object, to be mapped out of this environment via a given + * bridge.
  • + *
  • A proxy object, mapped into this environment via some bridge + * B1, and now to be mapped out of this environment via a + * given bridge B2.
  • + *
  • A proxy object, created as a remote object is mapped into this + * environment via a given bridge.
  • + *
+ * + *

The object actually registered may differ from the specified + * object that is passed as an argument. This enables an + * environment to work in a multi-threaded scenario, where two threads can + * call registerInterface for the same combination of + * oid and type at the same time; the race + * condition is solved by letting one of the calls register its argument + * object, ignoring the argument object of the + * other call, and letting both calls return the same + * object.

+ * + *

The registered object is held only weakly by the environment. After a + * call to registerInterface, a call to + * getRegisteredInterface only succeeds as long as the + * registered object is still strongly reachable, and the registered object + * has not been explicitly revoked by calling + * revokeInterface.

+ * + * @param object the object to register; must be non-null + * @param oid in-out parameter containing the OID of object. + * This must be a non-null reference to an array of length at least one; + * the zeroth element is used to pass the argument in and out. If the + * zeroth element is null on input, the OID will be computed and passed + * out (that is, the zeroth element will never be null upon normal + * return). + * @param type the UNO interface type to register. This argument must be + * non-null, and must denote a UNO interface type. The given + * object should implement this type. + * @return the registered object (may differ from the object + * passed in); will never be null + */ + Object registerInterface(Object object, String[] oid, Type type); + + /** + * Explicitly revokes a UNO interface facet. + * + *

Calls to registerInterface and + * revokeInterface must be paired. A facet is only removed + * from the environment when it has been revoked as often as it has been + * registered. This may change in the future, so that a facet would be + * removed upon the first call to revokeInterface (and calls to + * revokeInterface would no longer be necessary if the calling + * code does not want to control the temporal extent of the + * registration).

+ * + *

It is not an error if the specified facet is not registered at this + * environment (either because no corresponding object has ever been + * registered, or it has been explicitly revoked, or it is no longer + * strongly reachable). In such a case, this method simply does + * nothing.

+ * + * @param oid the OID of the object to revoke; must be non-null + * @param type the UNO interface type of the object to revoke. This + * argument must be non-null, and must denote a UNO interface type. + */ + void revokeInterface(String oid, Type type); + + /** + * Retrieves a registered object, specified by OID and UNO interface type. + * + * @param oid the OID of the object to retrieve; must be non-null + * @param type the UNO interface type of the object to retrieve. This + * argument must be non-null, and must denote a UNO interface type. + * @return the registered object, or null if none is found + */ + Object getRegisteredInterface(String oid, Type type); + + /** + * Retrieves the OID for a registered object. + * + * @param object a registered object; must be non-null + * @return the OID of the object; will never be null + */ + String getRegisteredObjectIdentifier(Object object); + + /** + * Lists the registered objects to System.out. + * + *

This is for debug purposes.

+ */ + void list(); +} diff --git a/ridljar/com/sun/star/uno/IMapping.java b/ridljar/com/sun/star/uno/IMapping.java new file mode 100644 index 0000000000..fdf6d8207f --- /dev/null +++ b/ridljar/com/sun/star/uno/IMapping.java @@ -0,0 +1,41 @@ +/* + * 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.uno; + +/** + * With a mapping objects can be mapped from one environment to another. + * + *

This interface exists for compatibility with the binary UNO API.

+ * + * @see com.sun.star.uno.IBridge + * + * @deprecated As of UDK 3.2, this interface is deprecated, without offering a + * replacement. + */ +@Deprecated +public interface IMapping { + /** + * Maps an interface from one environment to another. + * + * @param object source object that is to be mapped + * @param type description of the interface that is to be mapped + * @return the object mapped to the destination environment + */ + Object mapInterface(Object object, Type type); +} diff --git a/ridljar/com/sun/star/uno/IQueryInterface.java b/ridljar/com/sun/star/uno/IQueryInterface.java new file mode 100644 index 0000000000..7857d62054 --- /dev/null +++ b/ridljar/com/sun/star/uno/IQueryInterface.java @@ -0,0 +1,60 @@ +/* + * 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.uno; + +/** + * This is the delegator interface for Java objects implementing interfaces of + * an underlying UNO object. + * + *

Calls are delegated through the UnoRuntime to this + * interface. Implement this interface in case you want to customize the + * behaviour of UnoRuntime.queryInterface.

+ * + * @see com.sun.star.uno.UnoRuntime + */ +public interface IQueryInterface { + /** + * Returns the unique object identifier (OID) of the underlying UNO object. + * + * @return the OID of the underlying object + */ + String getOid(); + + /** + * Returns an object implementing the requested interface type. + * + * @param type the requested UNO interface type; must be a Type + * object representing a UNO interface type + * @return a reference to the requested UNO interface type if available, + * otherwise null + * @see com.sun.star.uno.UnoRuntime + */ + Object queryInterface(Type type); + + /** + * Tests if the given reference represents a facet of the underlying UNO + * object. + * + * @param object a reference to any Java object representing (a facet of) a + * UNO object; may be null + * @return true if and only if object is not + * null and represents the same UNO object as this object + */ + boolean isSame(Object object); +} diff --git a/ridljar/com/sun/star/uno/MappingException.java b/ridljar/com/sun/star/uno/MappingException.java new file mode 100644 index 0000000000..7670a790ad --- /dev/null +++ b/ridljar/com/sun/star/uno/MappingException.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.uno; + + +/** + * The mapping Exception. + * + *

The exception is replaced by the com.sun.star.lang.DisposedException.

+ * + * @see com.sun.star.uno.UnoRuntime + * @see com.sun.star.uno.IQueryInterface + * @see com.sun.star.uno.IBridge + * + * @deprecated since UDK 3.0.2 + */ +@Deprecated +public class MappingException extends com.sun.star.uno.RuntimeException { + /** + * Constructs an empty MappingException. + */ + public MappingException() { + super(); + } + + /** + * Constructs an MappingException with a detail message. + * + * @param message the detail message. + */ + public MappingException(String message) { + super(message); + } + + /** + * Constructs an MappingException with a detail message and a + * context. + * + * @param message the detail message. + * @param context the context. + */ + public MappingException(String message, Object context) { + super(message, context); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/Type.java b/ridljar/com/sun/star/uno/Type.java new file mode 100644 index 0000000000..0589b64cbb --- /dev/null +++ b/ridljar/com/sun/star/uno/Type.java @@ -0,0 +1,699 @@ +/* + * 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.uno; + +import java.util.HashMap; + +import com.sun.star.lib.uno.typedesc.TypeDescription; + +/** + * Represents the UNO built-in type TYPE. + * + *

The UNO type is not directly mapped to java.lang.Class for at + * least two reasons. For one, some UNO types (like UNSIGNED + * SHORT) do not have a matching Java class. For another, it can be + * necessary to describe a type which is unknown to the Java runtime system + * (for example, for delaying the need of a class, so that it is possible to + * generate it on the fly.)

+ * + *

A Type is uniquely determined by its type class (a + * TypeClass) and its type name (a String); these two + * will never be null. A Type may have an additional + * "z class" (a java.lang.Class), giving a Java class type that + * corresponds to the UNO type. Also, a Type can cache a type + * description (a com.sun.star.uno.typedesc.TypeDescription), which can be + * computed and set by TypeDescription.getTypeDescription. + */ +public class Type { + // The following private static members and static initializer must come + // first in the class definition, so that the class can be initialized + // successfully: + + private static final String TYPE_NAME_VOID = "void"; + private static final String TYPE_NAME_BOOLEAN = "boolean"; + private static final String TYPE_NAME_BYTE = "byte"; + private static final String TYPE_NAME_SHORT = "short"; + private static final String TYPE_NAME_UNSIGNED_SHORT = "unsigned short"; + private static final String TYPE_NAME_LONG = "long"; + private static final String TYPE_NAME_UNSIGNED_LONG = "unsigned long"; + private static final String TYPE_NAME_HYPER = "hyper"; + private static final String TYPE_NAME_UNSIGNED_HYPER = "unsigned hyper"; + private static final String TYPE_NAME_FLOAT = "float"; + private static final String TYPE_NAME_DOUBLE = "double"; + private static final String TYPE_NAME_CHAR = "char"; + private static final String TYPE_NAME_STRING = "string"; + private static final String TYPE_NAME_TYPE = "type"; + private static final String TYPE_NAME_ANY = "any"; + + // must be sorted same as TypeClass: + private static final String[] __typeClassToTypeName = new String[] { + TYPE_NAME_VOID, + TYPE_NAME_CHAR, + TYPE_NAME_BOOLEAN, + TYPE_NAME_BYTE, + TYPE_NAME_SHORT, + TYPE_NAME_UNSIGNED_SHORT, + TYPE_NAME_LONG, + TYPE_NAME_UNSIGNED_LONG, + TYPE_NAME_HYPER, + TYPE_NAME_UNSIGNED_HYPER, + TYPE_NAME_FLOAT, + TYPE_NAME_DOUBLE, + TYPE_NAME_STRING, + TYPE_NAME_TYPE, + TYPE_NAME_ANY + }; + + private static final class TypeInfo { + TypeInfo( + TypeClass thePrimary, TypeClass theAlternative, + boolean theSequenceComponentType) + { + primary = thePrimary; + alternative = theAlternative; + sequenceComponentType = theSequenceComponentType; + } + + final TypeClass primary; + final TypeClass alternative; + final boolean sequenceComponentType; + } + + private static final HashMap, TypeInfo> __javaClassToTypeClass = + new HashMap, TypeInfo>(); + static { + __javaClassToTypeClass.put( + void.class, new TypeInfo(TypeClass.VOID, TypeClass.VOID, false)); + __javaClassToTypeClass.put( + Void.class, new TypeInfo(TypeClass.VOID, TypeClass.VOID, false)); + __javaClassToTypeClass.put( + boolean.class, + new TypeInfo(TypeClass.BOOLEAN, TypeClass.BOOLEAN, true)); + __javaClassToTypeClass.put( + Boolean.class, + new TypeInfo(TypeClass.BOOLEAN, TypeClass.BOOLEAN, false)); + __javaClassToTypeClass.put( + byte.class, new TypeInfo(TypeClass.BYTE, TypeClass.BYTE, true)); + __javaClassToTypeClass.put( + Byte.class, new TypeInfo(TypeClass.BYTE, TypeClass.BYTE, false)); + __javaClassToTypeClass.put( + short.class, + new TypeInfo(TypeClass.SHORT, TypeClass.UNSIGNED_SHORT, true)); + __javaClassToTypeClass.put( + Short.class, + new TypeInfo(TypeClass.SHORT, TypeClass.UNSIGNED_SHORT, false)); + __javaClassToTypeClass.put( + int.class, + new TypeInfo(TypeClass.LONG, TypeClass.UNSIGNED_LONG, true)); + __javaClassToTypeClass.put( + Integer.class, + new TypeInfo(TypeClass.LONG, TypeClass.UNSIGNED_LONG, false)); + __javaClassToTypeClass.put( + long.class, + new TypeInfo(TypeClass.HYPER, TypeClass.UNSIGNED_HYPER, true)); + __javaClassToTypeClass.put( + Long.class, + new TypeInfo(TypeClass.HYPER, TypeClass.UNSIGNED_HYPER, false)); + __javaClassToTypeClass.put( + float.class, new TypeInfo(TypeClass.FLOAT, TypeClass.FLOAT, true)); + __javaClassToTypeClass.put( + Float.class, new TypeInfo(TypeClass.FLOAT, TypeClass.FLOAT, false)); + __javaClassToTypeClass.put( + double.class, + new TypeInfo(TypeClass.DOUBLE, TypeClass.DOUBLE, true)); + __javaClassToTypeClass.put( + Double.class, + new TypeInfo(TypeClass.DOUBLE, TypeClass.DOUBLE, false)); + __javaClassToTypeClass.put( + char.class, new TypeInfo(TypeClass.CHAR, TypeClass.CHAR, true)); + __javaClassToTypeClass.put( + Character.class, + new TypeInfo(TypeClass.CHAR, TypeClass.CHAR, false)); + __javaClassToTypeClass.put( + String.class, + new TypeInfo(TypeClass.STRING, TypeClass.STRING, true)); + __javaClassToTypeClass.put( + Type.class, new TypeInfo(TypeClass.TYPE, TypeClass.TYPE, true)); + __javaClassToTypeClass.put( + Any.class, new TypeInfo(TypeClass.ANY, TypeClass.ANY, true)); + __javaClassToTypeClass.put( + Object.class, + new TypeInfo(TypeClass.ANY, TypeClass.INTERFACE, true)); + } + + public static final Type VOID = new Type(void.class); + public static final Type CHAR = new Type(char.class); + public static final Type BOOLEAN = new Type(boolean.class); + public static final Type BYTE = new Type(byte.class); + public static final Type SHORT = new Type(short.class); + public static final Type UNSIGNED_SHORT = new Type( + TYPE_NAME_UNSIGNED_SHORT, TypeClass.UNSIGNED_SHORT); + public static final Type LONG = new Type(int.class); + public static final Type UNSIGNED_LONG = new Type( + TYPE_NAME_UNSIGNED_LONG, TypeClass.UNSIGNED_LONG); + public static final Type HYPER = new Type(long.class); + public static final Type UNSIGNED_HYPER = new Type( + TYPE_NAME_UNSIGNED_HYPER, TypeClass.UNSIGNED_HYPER); + public static final Type FLOAT = new Type(float.class); + public static final Type DOUBLE = new Type(double.class); + public static final Type STRING = new Type(String.class); + public static final Type TYPE = new Type(Type.class); + public static final Type ANY = new Type(Any.class); + + /** + * Constructs a new Type which defaults to VOID. + */ + public Type() { + init(null, void.class, false, false, false); + } + + /** + * Constructs a new Type with the given type class and type + * name. + * + * @param typeName the type name. Must not be null. + * @param typeClass the type class. Must not be null, and must + * match the typeName (for example, it is illegal to + * combine a typeName of "void" with a + * typeClass of BOOLEAN). + */ + public Type(String typeName, TypeClass typeClass) { + _typeClass = typeClass; + _typeName = typeName; + } + + /** + * Constructs a new Type from the given + * java.lang.Class. + * + *

This is equivalent to Type(zClass, false).

+ * + * @param zClass the Java class of this type. Must not be + * null. + */ + public Type(Class zClass) { + init(null, zClass, false, false, false); + } + + /** + * Constructs a new Type from the given + * java.lang.Class, handling ambiguous cases. + * + *

In certain cases, one Java class corresponds to two UNO types (e.g., + * the Java class short[].class corresponds to both a sequence + * of SHORT and a sequence of UNSIGNED SHORT in + * UNO). In such ambiguous cases, the parameter alternative + * controls which UNO type is chosen: + *

    + *
  • If the Java type is (an array type with element type) + * short or java.lang.Short: If + * alternative is false, the chosen UNO type is + * (a sequence type with element type) SHORT. If + * alternative is true, the chosen UNO type is + * (a sequence type with element type) UNSIGNED SHORT.
  • + * + *
  • If the Java type is (an array type with element type) + * int or java.lang.Integer: If + * alternative is false, the chosen UNO type is + * (a sequence type with element type) LONG. If + * alternative is true, the chosen UNO type is + * (a sequence type with element type) UNSIGNED LONG.
  • + * + *
  • If the Java type is (an array type with element type) + * long or java.lang.Long: If + * alternative is false, the chosen UNO type is + * (a sequence type with element type) HYPER. If + * alternative is true, the chosen UNO type is + * (a sequence type with element type) UNSIGNED HYPER.
  • + * + *
  • If the Java type is (an array type with element type) + * java.lang.Object: If alternative is + * false, the chosen UNO type is (a sequence type with + * element type) ANY. If alternative is + * true, the chosen UNO type is (a sequence type with element + * type) com.sun.star.uno.XInterface.
  • + *
+ *

In all other cases, the value of alternative is + * ignored.

+ * + *

This constructor cannot be used to create Type instances + * that represent (sequences of) instantiated polymorphic struct types.

+ * + * @param zClass the Java class of this type; must not be null + * @param alternative controls which UNO type to choose in case of + * ambiguities + * + * @since UDK 3.2.0 + */ + public Type(Class zClass, boolean alternative) { + init(null, zClass, alternative, false, false); + } + + private Type( + Class zClass, boolean alternative, boolean sequenceComponentType) + { + init(null, zClass, alternative, false, sequenceComponentType); + } + + /** + * Constructs a new Type from the given type description. + * + * For internal URE use only. Not to be used by client code. + * + * @param typeDescription a type description. Must not be + * null. + */ + public Type(TypeDescription typeDescription) { + _typeName = typeDescription.getTypeName(); + _typeClass = typeDescription.getTypeClass(); + _iTypeDescription = typeDescription; + } + + /** + * Constructs a new Type with the given type name. + * + * @param typeName the name of this type; must not be null. + */ + public Type(String typeName) { + if (typeName.startsWith("[]")) { + _typeName = typeName; + _typeClass = TypeClass.SEQUENCE; + return; + } + for (int i = 0; i < __typeClassToTypeName.length; ++i) { + if (__typeClassToTypeName[i].equals(typeName)) { + _typeName = typeName; + _typeClass = TypeClass.fromInt(i); + return; + } + } + int i = typeName.indexOf('<'); + try { + init( + typeName, + Class.forName(i < 0 ? typeName : typeName.substring(0, i)), + false, i >= 0, false); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Constructs a new Type with the given type class. + * + * @param typeClass the type class of this type; must not be + * null. Only type classes for simple types are allowed + * here. + * + * @throws IllegalArgumentException if the given typeClass is + * not simple (for example, a struct or an interface type). This + * constructor could not find out the type name in such a case. + */ + public Type(TypeClass typeClass) { + if(__isTypeClassPrimitive(typeClass)) { + _typeClass = typeClass; + _typeName = __typeClassToTypeName[typeClass.getValue()]; + } + else + throw new IllegalArgumentException(typeClass + " is not primitive"); + } + + /** + * Gets the type class. + * + * @return the type class. Will never be null, but might be + * UNKNOWN. + */ + public TypeClass getTypeClass() { + return _typeClass; + } + + /** + * Gets the type name. + * + * @return the type name; will never be null + */ + public String getTypeName() { + return _typeName; + } + + /** + * Gets the Java class. + * + * @return the type name; may be null in extreme situations + * (inconsistent TypeClass, error loading a class) + */ + public Class getZClass() { + synchronized (this) { + if (_class == null) { + _class = determineClass(); + } + } + return _class; + } + + /** + * Gives the type description of this type. + * + * For internal URE use only. Not to be used by client code. + * + * @return the type description; may be null + */ + public TypeDescription getTypeDescription() { + return _iTypeDescription; + } + + /** + * Sets the type description for this type. + * + * For internal URE use only. Not to be used by client code. + * + * @param typeDescription the type description + */ + public void setTypeDescription(TypeDescription typeDescription) { + _iTypeDescription = typeDescription; + } + + /** + * Determines whether this UNO type is a supertype of another UNO type. + * + * UNO only defines the following supertype relations: + * (1) A struct type t1 is a supertype of a struct type t2, if either t1 + * and t2 are the same, or t1 is a direct or indirect parent of t2. + * (2) An exception type t1 is a supertype of an exception type t2, if + * either t1 and t2 are the same, or t1 is a direct or indirect parent + * of t2. + * (3) An interface type t1 is a supertype of an interface type t2, if + * either t1 and t2 are the same, or t1 is a direct or indirect parent + * of t2. + * + * Following the conventions of the Java UNO language binding, + * com.sun.star.uno.Exception is not considered a supertype of + * com.sun.star.uno.RuntimeException or any exception type derived from + * com.sun.star.uno.RuntimeException. + * + * @param type some Type + * @return true if this type is a supertype of the given type + * + * @since UDK 3.2.0 + */ + public boolean isSupertypeOf(Type type) { + if (_typeClass != type._typeClass) { + return false; + } + switch (_typeClass.getValue()) { + case TypeClass.SEQUENCE_value: + case TypeClass.ENUM_value: + return _typeName.equals(type._typeName); + + case TypeClass.STRUCT_value: + // This check exploits the fact that an instantiated polymorphic + // struct type may not be the direct base of a struct type: + if (_typeName.indexOf('<') >= 0 || type._typeName.indexOf('<') >= 0) + { + return _typeName.equals(type._typeName); + } + // fall-through + case TypeClass.EXCEPTION_value: + case TypeClass.INTERFACE_value: + Class c1 = getZClass(); + Class c2 = type.getZClass(); + return c1 != null && c2 != null && c1.isAssignableFrom(c2); + + default: + return true; + } + } + + /** + * Indicates whether some other object is equal to this one. + * + * @param obj the reference object with which to compare. + * @return true if this object is the same as the obj argument; + * false otherwise. + * @see java.lang.Object#equals + */ + @Override + public boolean equals(Object obj) { + return obj instanceof Type + && _typeClass == ((Type) obj)._typeClass + && _typeName.equals(((Type) obj)._typeName); + } + + /** + * 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() { + return _typeName.hashCode(); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return "Type[" + _typeName + "]"; + } + + private void init( + String name, Class zClass, boolean alternative, boolean arguments, + boolean sequenceComponentType) + { + TypeInfo info = __javaClassToTypeClass.get(zClass); + if (info != null) { + if (sequenceComponentType && !info.sequenceComponentType) { + throw new IllegalArgumentException( + zClass + " cannot be sequence component type"); + } + // info only contains primitive type classes, except for + // TypeClass.INTERFACE, which stands for XInterface (the alternative + // interpretation of java.lang.Object): + _typeClass = alternative ? info.alternative : info.primary; + _typeName = _typeClass == TypeClass.INTERFACE + ? XInterface.class.getName() + : __typeClassToTypeName[_typeClass.getValue()]; + // do not assign _class from zClass, as _class should always be + // normalized (e.g., boolean.class instead of + // java.lang.Boolean.class); getZClass will later calculate the + // correct class when needed + } else if (zClass.isArray()) { + Type t = new Type(zClass.getComponentType(), alternative, true); + _typeClass = t.getTypeClass() != TypeClass.UNKNOWN + ? TypeClass.SEQUENCE : TypeClass.UNKNOWN; + _typeName = "[]" + t.getTypeName(); + // do not assign _class from zClass, as _class should always be + // normalized (e.g., boolean[].class instead of + // java.lang.Boolean[].class); getZClass will later calculate the + // correct class when needed + } else if (Enum.class.isAssignableFrom(zClass)) { + _typeClass = zClass != Enum.class + ? TypeClass.ENUM : TypeClass.UNKNOWN; + _typeName = zClass.getName(); + _class = zClass; + } else if (Throwable.class.isAssignableFrom(zClass)) { + _typeClass + = com.sun.star.uno.Exception.class.isAssignableFrom(zClass) + || com.sun.star.uno.RuntimeException.class.isAssignableFrom( + zClass) + ? TypeClass.EXCEPTION : TypeClass.UNKNOWN; + _typeName = zClass.getName(); + _class = zClass; + } else if (zClass.isInterface()) { + _typeClass = XInterface.class.isAssignableFrom(zClass) + ? TypeClass.INTERFACE : TypeClass.UNKNOWN; + _typeName = zClass.getName(); + _class = zClass; + } else if (XInterface.class.isAssignableFrom(zClass)) { + // This case is needed by code that uses this constructor to + // calculate the UNO type corresponding to a Java object: + _typeClass = TypeClass.INTERFACE; + _typeName = XInterface.class.getName(); + _class = XInterface.class; + } else { + // assert zClass != Object.class && !zClass.isPrimitive(); + _typeClass = TypeClass.STRUCT; + _typeName = name == null ? zClass.getName() : name; + _class = zClass; + } + if (arguments && _typeClass != TypeClass.STRUCT) { + throw new IllegalArgumentException( + zClass + " cannot have type arguments"); + } + } + + private Class determineClass() { + switch (_typeClass.getValue()) { + case TypeClass.VOID_value: + return _typeName.equals(TYPE_NAME_VOID) ? void.class : null; + + case TypeClass.BOOLEAN_value: + return _typeName.equals(TYPE_NAME_BOOLEAN) ? boolean.class : null; + + case TypeClass.BYTE_value: + return _typeName.equals(TYPE_NAME_BYTE) ? byte.class : null; + + case TypeClass.SHORT_value: + return _typeName.equals(TYPE_NAME_SHORT) ? short.class : null; + + case TypeClass.UNSIGNED_SHORT_value: + return _typeName.equals(TYPE_NAME_UNSIGNED_SHORT) + ? short.class : null; + + case TypeClass.LONG_value: + return _typeName.equals(TYPE_NAME_LONG) ? int.class : null; + + case TypeClass.UNSIGNED_LONG_value: + return _typeName.equals(TYPE_NAME_UNSIGNED_LONG) ? int.class : null; + + case TypeClass.HYPER_value: + return _typeName.equals(TYPE_NAME_HYPER) ? long.class : null; + + case TypeClass.UNSIGNED_HYPER_value: + return _typeName.equals(TYPE_NAME_UNSIGNED_HYPER) + ? long.class : null; + + case TypeClass.FLOAT_value: + return _typeName.equals(TYPE_NAME_FLOAT) ? float.class : null; + + case TypeClass.DOUBLE_value: + return _typeName.equals(TYPE_NAME_DOUBLE) ? double.class : null; + + case TypeClass.CHAR_value: + return _typeName.equals(TYPE_NAME_CHAR) ? char.class : null; + + case TypeClass.STRING_value: + return _typeName.equals(TYPE_NAME_STRING) ? String.class : null; + + case TypeClass.TYPE_value: + return _typeName.equals(TYPE_NAME_TYPE) ? Type.class : null; + + case TypeClass.ANY_value: + return _typeName.equals(TYPE_NAME_ANY) ? Object.class : null; + + case TypeClass.SEQUENCE_value: + StringBuffer buf = new StringBuffer(); + int offset = 0; + for (; _typeName.startsWith("[]", offset); offset += "[]".length()) + { + buf.append('['); + } + if (buf.length() == 0) { + return null; + } + String base = _typeName.substring(offset); + if (base.equals(TYPE_NAME_VOID)) { + buf.append('V'); + } else if (base.equals(TYPE_NAME_BOOLEAN)) { + buf.append('Z'); + } else if (base.equals(TYPE_NAME_BYTE)) { + buf.append('B'); + } else if (base.equals(TYPE_NAME_SHORT) + || base.equals(TYPE_NAME_UNSIGNED_SHORT)) { + buf.append('S'); + } else if (base.equals(TYPE_NAME_LONG) + || base.equals(TYPE_NAME_UNSIGNED_LONG)) { + buf.append('I'); + } else if (base.equals(TYPE_NAME_HYPER) + || base.equals(TYPE_NAME_UNSIGNED_HYPER)) { + buf.append('J'); + } else if (base.equals(TYPE_NAME_FLOAT)) { + buf.append('F'); + } else if (base.equals(TYPE_NAME_DOUBLE)) { + buf.append('D'); + } else if (base.equals(TYPE_NAME_CHAR)) { + buf.append('C'); + } else if (base.equals(TYPE_NAME_STRING)) { + buf.append("Ljava.lang.String;"); + } else if (base.equals(TYPE_NAME_TYPE)) { + buf.append("Lcom.sun.star.uno.Type;"); + } else if (base.equals(TYPE_NAME_ANY)) { + buf.append("Ljava.lang.Object;"); + } else { + int args = base.indexOf('<'); + if (args >= 0) { + base = base.substring(0, args); + } + Class c; + try { + c = Class.forName(base); + } catch (ClassNotFoundException e) { + return null; + } + if (args < 0 && new Type(c).getTypeClass() == TypeClass.UNKNOWN) + { + return null; + } + buf.append('L'); + buf.append(base); + buf.append(';'); + } + try { + return Class.forName(buf.toString()); + } catch (ClassNotFoundException e) { + return null; + } + + case TypeClass.ENUM_value: + case TypeClass.EXCEPTION_value: + case TypeClass.INTERFACE_value: + { + Class c; + try { + c = Class.forName(_typeName); + } catch (ClassNotFoundException e) { + return null; + } + return new Type(c).equals(this) ? c : null; + } + + case TypeClass.STRUCT_value: + { + int args = _typeName.indexOf('<'); + Class c; + try { + c = Class.forName( + args < 0 ? _typeName : _typeName.substring(0, args)); + } catch (ClassNotFoundException e) { + return null; + } + return args >= 0 || new Type(c).equals(this) ? c : null; + } + + default: + return null; + } + } + + private static boolean __isTypeClassPrimitive(TypeClass typeClass) { + return typeClass.getValue() < __typeClassToTypeName.length; + } + + private TypeClass _typeClass; // TODO should be final + private String _typeName; // TODO should be final + + private Class _class; + private TypeDescription _iTypeDescription; +} diff --git a/ridljar/com/sun/star/uno/UnoRuntime.java b/ridljar/com/sun/star/uno/UnoRuntime.java new file mode 100644 index 0000000000..9c2eedbd4e --- /dev/null +++ b/ridljar/com/sun/star/uno/UnoRuntime.java @@ -0,0 +1,726 @@ +/* + * 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.uno; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; +import java.util.WeakHashMap; + +import com.sun.star.lib.uno.typedesc.FieldDescription; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.lib.util.WeakMap; + +/** + * The central class needed for implementing or using UNO components in Java. + * + *

The methods queryInterface and areSame delegate + * calls to the implementing objects and are used instead of casts, + * instanceof, ==, and equals.

+ * + *

For historic reasons, this class is not final, and has a + * public constructor. These artifacts are considered mistakes, + * which might be corrected in a future version of this class, so client code + * should not rely on them.

+ * + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IEnvironment + * @see com.sun.star.uno.IQueryInterface + */ +public class UnoRuntime { + /** + * @deprecated As of UDK 3.2.0, do not create instances of this class. + * It is considered a historic mistake to have a public + * constructor for this class, which only has static members. + * Also, this class might be changed to become final in a + * future version. + */ + @Deprecated + public UnoRuntime() {} + + /** + * Generates a worldwide unique identifier string. + * + *

It is guaranteed that every invocation of this method generates a new + * ID, which is unique within the VM. The quality of “worldwide + * unique” will depend on the actual implementation, you should look + * at the source to determine if it meets your requirements.

+ * + * @return a unique String + */ + public static String getUniqueKey() { + synchronized (uniqueKeyLock) { + if (uniqueKeyCount == Long.MAX_VALUE) { + long time; + for (time = System.currentTimeMillis(); time == uniqueKeyTime;) + { + // Conservatively sleep for 100 millisecond to wait for + // System.currentTimeMillis() to change: + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + uniqueKeyTime = time; + uniqueKeyCount = Long.MIN_VALUE; + } + return uniqueKeyHostPrefix + Long.toString(uniqueKeyTime, 16) + ":" + + Long.toString(uniqueKeyCount++, 16); + } + } + + /** + * Generates a worldwide unique object identifier (OID) for the given + * Java object. + * + *

It is guaranteed that subsequent calls to this method with the same + * Java object will give the same ID.

+ * + *

This method is generally of little use for client code. It should be + * considered a mistake that this method is published at all.

+ * + * @param object any object for which an OID shall be generated; must not be + * null + * @return the generated OID + * @see com.sun.star.uno.IQueryInterface#getOid + */ + public static String generateOid(Object object) { + String oid = null; + if (object instanceof IQueryInterface) { + oid = ((IQueryInterface) object).getOid(); + } + if (oid == null) { + synchronized (oidMap) { + oid = oidMap.get(object); + if (oid == null) { + oid = UUID.randomUUID().toString() + oidSuffix; + oidMap.put(object, oid); + } + } + } + return oid; + } + + /** + * Queries the given UNO object for the given UNO interface type. + * + *

This method returns null in case the given UNO object + * does not support the given UNO interface type (or is itself + * null). Otherwise, a reference to a Java object implementing + * the Java interface type corresponding to the given UNO interface is + * returned. In the latter case, it is unspecified whether the returned + * Java object is the same as the given object, or is another facet of that + * UNO object.

+ * + * @param type the requested UNO interface type; must be a Type + * object representing a UNO interface type + * @param object a reference to any Java object representing (a facet of) a + * UNO object; may be null + * @return a reference to the requested UNO interface type if available, + * otherwise null + * @see com.sun.star.uno.IQueryInterface#queryInterface + */ + public static Object queryInterface(Type type, Object object) { + // Gracefully handle those situations where the passed in UNO object is + // wrapped in an Any. Strictly speaking, such a situation constitutes a + // bug, but it is anticipated that such situations will arise quite + // often in practice (especially since UNO Anys containing an XInterface + // reference are not wrapped in a Java Any, but UNO Anys containing any + // other interface reference are wrapped in a Java Any, which can lead + // to confusion). + if (object instanceof Any) { + Any a = (Any) object; + if (a.getType().getTypeClass() == TypeClass.INTERFACE) { + object = a.getObject(); + } + } + if (object instanceof IQueryInterface) { + object = ((IQueryInterface) object).queryInterface(type); + if (object instanceof Any) { + Any a = (Any) object; + object = a.getType().getTypeClass() == TypeClass.INTERFACE + ? a.getObject() : null; + } + } + // Ensure that the object implements the requested interface type: + Class c = type.getZClass(); + if (c == null || !c.isInstance(object)) { + object = null; + } + return object; + } + + /** + * Queries the given UNO object for the given Java class (which must + * represent a UNO interface type). + * + * @param the requested UNO interface type. + * @param zInterface a Java class representing a UNO interface type + * @param object a reference to any Java object representing (a facet of) a + * UNO object; may be null + * @return a reference to the requested UNO interface type if available, + * otherwise null + * @see #queryInterface(Type, Object) + */ + @SuppressWarnings("unchecked") + public static T queryInterface(Class zInterface, Object object) { + return (T) queryInterface(new Type(zInterface), object); + } + + /** + Tests two UNO ANY values for equality. + +

Two UNO values are equal if and only if they are of the + same UNO type t, and they meet the following condition, + depending on t:

+
    +
  • If t is a primitive type, then both values must denote + the same element of the set of values of t.
  • + +
  • If t is a structured type, then both values must + recursively contain corresponding values that are equal.
  • + +
  • If t is an interface type, then the two values must be + either both null references, or both references to the same UNO + object.
  • +
+ + @param any1 a Java value representing a UNO ANY value. + + @param any2 a Java value representing a UNO ANY value. + + @return true if and only if the two arguments represent + equal UNO values. + */ + public static boolean areSame(Object any1, Object any2) { + Any a1 = Any.complete(any1); + Any a2 = Any.complete(any2); + Type t = a1.getType(); + if (!a2.getType().equals(t)) { + return false; + } + Object v1 = a1.getObject(); + Object v2 = a2.getObject(); + switch (t.getTypeClass().getValue()) { + case TypeClass.VOID_value: + return true; + case TypeClass.BOOLEAN_value: + case TypeClass.BYTE_value: + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + case TypeClass.HYPER_value: + case TypeClass.UNSIGNED_HYPER_value: + case TypeClass.FLOAT_value: + case TypeClass.DOUBLE_value: + case TypeClass.CHAR_value: + case TypeClass.STRING_value: + case TypeClass.TYPE_value: + return v1.equals(v2); + case TypeClass.SEQUENCE_value: + int n = Array.getLength(v1); + if (n != Array.getLength(v2)) { + return false; + } + for (int i = 0; i < n; ++i) { + // Recursively using areSame on Java values that are (boxed) + // elements of Java arrays representing UNO sequence values, + // instead of on Java values that are representations of UNO ANY + // values, works by chance: + if (!areSame(Array.get(v1, i), Array.get(v2, i))) { + return false; + } + } + return true; + case TypeClass.ENUM_value: + return v1 == v2; + case TypeClass.STRUCT_value: + case TypeClass.EXCEPTION_value: + FieldDescription[] fs; + try { + fs = TypeDescription.getTypeDescription(t). + getFieldDescriptions(); + } catch (ClassNotFoundException e) { + throw new java.lang.RuntimeException(e); + } + for (int i = 0; i< fs.length; ++i) { + Type ft = new Type(fs[i].getTypeDescription()); + try { + // Recursively using areSame on Java values that are (boxed) + // fields of Java classes representing UNO struct or + // exception values, instead of on Java values that are + // representations of UNO ANY values, works by chance: + if (!areSame( + completeValue(ft, fs[i].getField().get(v1)), + completeValue(ft, fs[i].getField().get(v2)))) + { + return false; + } + } catch (IllegalAccessException e) { + throw new java.lang.RuntimeException(e); + } + } + return true; + case TypeClass.INTERFACE_value: + return v1 == v2 + || (v1 instanceof IQueryInterface + && ((IQueryInterface) v1).isSame(v2)) + || (v2 instanceof IQueryInterface + && ((IQueryInterface) v2).isSame(v1)); + default: + throw new java.lang.RuntimeException( + "com.sun.star.uno.Any has bad com.sun.star.uno.TypeClass"); + } + } + + /** + Complete a UNO value (make sure it is no invalid null + value). + +

This is useful for members of parameterized type of instantiated + polymorphic struct types, as null is a valid value there + (and only there, for all types except ANY and interface + types).

+ + @param type a non-void, non-exception UNO type. + + @param value a Java value representing a UNO value of the given UNO type, + or null. + + @return the given value, or the neutral value of the given type, if the + given value was an invalid null value. + + @since UDK 3.2.3 + */ + public static final Object completeValue(Type type, Object value) { + if (value != null) { + return value; + } + switch (type.getTypeClass().getValue()) { + case TypeClass.BOOLEAN_value: + return Boolean.FALSE; + case TypeClass.BYTE_value: + return Byte.valueOf((byte) 0); + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return Short.valueOf((short) 0); + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + return Integer.valueOf(0); + case TypeClass.HYPER_value: + case TypeClass.UNSIGNED_HYPER_value: + return Long.valueOf(0); + case TypeClass.FLOAT_value: + return Float.valueOf(0.0f); + case TypeClass.DOUBLE_value: + return Double.valueOf(0.0); + case TypeClass.CHAR_value: + return Character.valueOf('\u0000'); + case TypeClass.STRING_value: + return ""; + case TypeClass.TYPE_value: + return Type.VOID; + case TypeClass.ANY_value: + case TypeClass.INTERFACE_value: + return null; + case TypeClass.SEQUENCE_value: + return Array.newInstance(type.getZClass().getComponentType(), 0); + case TypeClass.STRUCT_value: + try { + return type.getZClass().getConstructor((Class[]) null). + newInstance((Object[]) null); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.lang.RuntimeException(e); + } + case TypeClass.ENUM_value: + try { + return type.getZClass().getMethod("getDefault", (Class[]) null). + invoke(null, (Object[]) null); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.lang.RuntimeException(e); + } + default: + throw new IllegalArgumentException( + "com.sun.star.uno.UnoRuntime.completeValue called with bad" + + " com.sun.star.uno.Type"); + } + } + + /** + * Gets the current context of the current thread, or null if + * no context has been set for the current thread. + * + *

The current context is thread local, which means that this method + * returns the context that was last set for this thread.

+ * + * @return the current context of the current thread, or null + * if no context has been set for the current thread + */ + public static XCurrentContext getCurrentContext() { + return currentContext.get(); + } + + /** + * Sets the current context for the current thread. + * + *

The current context is thread local. To support a stacking behaviour, + * every function that sets the current context should reset it to the + * original value when exiting (for example, within a finally + * block).

+ * + * @param context the context to be set; if null, any + * previously set context will be removed + */ + public static void setCurrentContext(XCurrentContext context) { + if (context == null) { + currentContext.remove(); + } else { + currentContext.set(context); + } + } + + /** + * Retrieves an environment of type name with context + * context. + * + *

Environments are held weakly by this class. If the requested + * environment already exists, this methods simply returns it. Otherwise, + * this method looks for it under + * com.sun.star.lib.uno.environments.name.name_environment.

+ * + * @param name the name of the environment + * @param context the context of the environment + * @throws Exception if something goes awry. + * @return an environment. + * @see com.sun.star.uno.IEnvironment + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IEnvironment getEnvironment(String name, Object context) + throws java.lang.Exception + { + synchronized (environments) { + IEnvironment env = WeakMap.getValue( + environments.get(name + context)); + if (env == null) { + Class c = Class.forName( + "com.sun.star.lib.uno.environments." + name + "." + name + + "_environment"); + Constructor ctor = c.getConstructor( + new Class[] { Object.class }); + env = (IEnvironment) ctor.newInstance(new Object[] { context }); + environments.put(name + context, env); + } + return env; + } + } + + /** + * Gets a bridge from environment from to environment + * to. + * + *

Creates a new bridge, if the requested bridge does not yet exist, and + * hands the arguments to the bridge.

+ * + *

If the requested bridge does not exist, it is searched for in package + * com.sun.star.lib.uno.bridges.from_to; + * and the root classpath as + * from_to_bridge.

+ * + * @param from the source environment + * @param to the target environment + * @param args the initial arguments for the bridge + * @throws Exception if something goes awry. + * @return the requested bridge + * @see #getBridgeByName + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IEnvironment + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IBridge getBridge( + IEnvironment from, IEnvironment to, Object[] args) + throws java.lang.Exception + { + synchronized (bridges) { + String name = from.getName() + "_" + to.getName(); + String hashName = from.getName() + from.getContext() + "_" + + to.getName() + to.getContext(); + IBridge bridge = WeakMap.getValue(bridges.get(hashName)); + if(bridge == null) { + Class zClass = null; + String className = name + "_bridge"; + try { + zClass = Class.forName(className); + } catch (ClassNotFoundException e) { + className = "com.sun.star.lib.uno.bridges." + name + "." + + className; + zClass = Class.forName(className); + } + Class[] signature = { + IEnvironment.class, IEnvironment.class, args.getClass() }; + Constructor constructor = zClass.getConstructor(signature); + Object[] iargs = { from, to, args }; + bridge = (IBridge) constructor.newInstance(iargs); + bridges.put(hashName, bridge); + } + return bridge; + } + } + + /** + * Gets a bridge from environment from to environment + * to. + * + *

Creates a new bridge, if the requested bridge does not yet exist, and + * hands the arguments to the bridge.

+ * + *

If the requested bridge does not exist, it is searched for in package + * com.sun.star.lib.uno.bridges.from_to; + * and the root classpath as + * from_to_bridge. The used environments + * are retrieved through getEnvironment.

+ * + * @param from the name of the source environment + * @param fromContext the context for the source environment + * @param to the name of the target environment + * @param toContext the context for the target environment + * @param args the initial arguments for the bridge + * @throws Exception if something goes awry. + * @return the requested bridge + * @see #getBridge + * @see #getEnvironment + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IEnvironment + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IBridge getBridgeByName( + String from, Object fromContext, String to, Object toContext, + Object[] args) throws java.lang.Exception + { + return getBridge( + getEnvironment(from, fromContext), getEnvironment(to, toContext), + args); + } + + /** + * Returns an array of all active bridges. + * + * @return an array of IBridge objects + * @see com.sun.star.uno.IBridge + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IBridge[] getBridges() { + ArrayList l = new ArrayList(); + synchronized (bridges) { + for (Iterator> i = bridges.values().iterator(); i.hasNext();) { + IBridge o = WeakMap.getValue(i.next()); + if (o != null) { + l.add(o); + } + } + } + return l.toArray(new IBridge[l.size()]); + } + + /** + * Gets a mapping from environment from to environment + * to. + * + *

Mappings are like bridges, except that with mappings one can only map + * in one direction. Mappings are here for compatibility with the binary + * UNO API. Mappings are implemented as wrappers around bridges.

+ * + * @param from the source environment + * @param to the target environment + * @throws Exception if something goes awry. + * @return the requested mapping + * @see com.sun.star.uno.IEnvironment + * @see com.sun.star.uno.IMapping + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IMapping getMapping(IEnvironment from, IEnvironment to) + throws java.lang.Exception + { + IBridge bridge; + try { + bridge = getBridge(from, to, null); + } + catch (ClassNotFoundException e) { + bridge = new BridgeTurner(getBridge(to, from, null)); + } + return new MappingWrapper(bridge); + } + + /** + * Gets a mapping from environment from to environment + * to. + * + *

The used environments are retrieved through + * getEnvironment.

+ * + * @param from the name of the source environment + * @param to the name of the target environment + * @throws Exception if something goes awry. + * @return the requested mapping + * @see #getEnvironment + * @see #getMapping + * @see com.sun.star.uno.IMapping + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IMapping getMappingByName(String from, String to) + throws java.lang.Exception + { + return getMapping(getEnvironment(from, null), getEnvironment(to, null)); + } + + /** + * Resets this UnoRuntime to its initial state. + * + *

Releases all references to bridges and environments.

+ * + * @return true if another thread didn't re-insert some bridge or + * environment before the method returns. Why that information + * would be useful is anybody's guess. + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static boolean reset() { + synchronized (bridges) { + for (Iterator> i = bridges.values().iterator(); i.hasNext();) { + IBridge b = WeakMap.getValue(i.next()); + if (b != null) { + // The following call to dispose was originally made to + // com.sun.star.lib.sandbox.Disposable.dispose, which cannot + // throw an InterruptedException or IOException: + try { + b.dispose(); + } catch (InterruptedException e) { + Thread.currentThread(); + Thread.interrupted(); + throw new RuntimeException( + "Unexpected exception in UnoRuntime.reset: " + e); + } catch (IOException e) { + throw new RuntimeException( + "Unexpected exception in UnoRuntime.reset: " + e); + } + } + } + bridges.clear(); + } + environments.clear(); + return bridges.isEmpty() && environments.isEmpty(); + } + + /** + * @deprecated As of UDK 3.2.0, do not use this internal field. + */ + @Deprecated + public static final boolean DEBUG = false; + + private static final class BridgeTurner implements IBridge { + public BridgeTurner(IBridge bridge) { + this.bridge = bridge; + } + + public Object mapInterfaceTo(Object object, Type type) { + return bridge.mapInterfaceFrom(object, type); + } + + public Object mapInterfaceFrom(Object object, Type type) { + return bridge.mapInterfaceTo(object, type); + } + + public IEnvironment getSourceEnvironment() { + return bridge.getTargetEnvironment(); + } + + public IEnvironment getTargetEnvironment() { + return bridge.getSourceEnvironment(); + } + + public void acquire() { + bridge.acquire(); + } + + public void release() { + bridge.release(); + } + + public void dispose() throws InterruptedException, IOException { + bridge.dispose(); + } + + private final IBridge bridge; + } + + private static final class MappingWrapper implements IMapping { + public MappingWrapper(IBridge bridge) { + this.bridge = bridge; + } + + public Object mapInterface(Object object, Type type) { + return bridge.mapInterfaceTo(object, type); + } + + private final IBridge bridge; + } + + private static final WeakHashMap oidMap = new WeakHashMap(); + private static final String uniqueKeyHostPrefix + = Integer.toString(new Object().hashCode(), 16) + ":"; + private static final Object uniqueKeyLock = new Object(); + private static long uniqueKeyTime = System.currentTimeMillis(); + private static long uniqueKeyCount = Long.MIN_VALUE; + + private static final String oidSuffix = ";java[];" + getUniqueKey(); + + private static final ThreadLocal currentContext = new ThreadLocal(); + + private static final WeakMap environments = new WeakMap(); + private static final WeakMap bridges = new WeakMap(); +} diff --git a/ridljar/com/sun/star/uno/WeakReference.java b/ridljar/com/sun/star/uno/WeakReference.java new file mode 100644 index 0000000000..a6b171ac2e --- /dev/null +++ b/ridljar/com/sun/star/uno/WeakReference.java @@ -0,0 +1,154 @@ +/* -*- 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.uno; + +/** + * This class holds weak reference to an object. + * + *

It actually holds a reference to a com.sun.star.XAdapter + * implementation and obtains a hard reference if necessary. + */ +public class WeakReference +{ + private OWeakRefListener m_listener; + // There is no default constructor. Every instance must register itself with the + // XAdapter interface, which is done in the constructors. Assume we have this code + // WeakReference ref= new WeakReference(); + // ref = someOtherWeakReference; + // + // ref would not be notified (XReference.dispose()) because it did not register + // itself. Therefore the XAdapter would be kept alive although this is not + // necessary. + + /** + * Creates an instance of this class. + * + * @param obj another instance that is to be copied. + */ + public WeakReference(WeakReference obj) + { + if (obj == null) { + return; + } + Object weakImpl = obj.get(); + if (weakImpl == null) { + return; + } + XWeak weak = UnoRuntime.queryInterface(XWeak.class, weakImpl); + if (weak != null) { + XAdapter adapter = weak.queryAdapter(); + if (adapter != null) + m_listener = new OWeakRefListener(adapter); + } + } + + /** + * Creates an instance of this class. + * + * @param obj XWeak implementation. + */ + public WeakReference(Object obj) + { + XWeak weak= UnoRuntime.queryInterface(XWeak.class, obj); + if (weak != null) + { + XAdapter adapter= weak.queryAdapter(); + if (adapter != null) + m_listener= new OWeakRefListener(adapter); + } + } + + /** + * Returns a hard reference to the object that is kept weak by this class. + * + * @return a hard reference to the XWeak implementation. + */ + public Object get() + { + if (m_listener != null) + return m_listener.get(); + return null; + } +} + +/** + * Implementation of com.sun.star.uno.XReference for use with WeakReference. + * + *

It keeps the XAdapter implementation and registers always with it. + * Deregistering occurs on notification by the adapter and the adapter is + * released.

+ */ +class OWeakRefListener implements XReference +{ + private XAdapter m_adapter; + + /** + * The constructor registered this object with adapter. + * + * @param adapter the XAdapter implementation. + */ + OWeakRefListener( XAdapter adapter) + { + m_adapter= adapter; + m_adapter.addReference(this); + } + + /** + * Method of com.sun.star.uno.XReference. + * + *

When called, it deregisters this object with the adapter and releases + * the reference to it.

+ */ + synchronized public void dispose() + { + if (m_adapter != null) + { + m_adapter.removeReference(this); + m_adapter= null; + } + } + + /** + * Obtains a hard reference to the object which is kept weak by the adapter + * and returns it. + * + * @return hard reference to the otherwise weakly kept object. + */ + synchronized Object get() + { + Object retVal= null; + if (m_adapter != null) + { + retVal= m_adapter.queryAdapted(); + if (retVal == null) + { + // If this object registered as listener with XAdapter while it was notifying + // the listeners then this object might not have been notified. If queryAdapted + // returned null then the weak kept object is dead and the listeners have already + // been notified. And we missed it. + m_adapter.removeReference(this); + m_adapter= null; + } + } + return retVal; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3